home *** CD-ROM | disk | FTP | other *** search
/ SPACE 1 / SPACE - Library 1 - Volume 1.iso / program / 385 / arpbook5 / chap_5.doc next >
Text File  |  1985-11-19  |  114KB  |  2,575 lines

  1.    Atari ST Machine Specific Programming In Assembly
  2.    
  3. Chapter 5: Performance Testing
  4.             
  5.  
  6. The Never Finished Theory
  7.  
  8.      In a recent magazine article, the author stated that no 
  9. program is ever finished.  I have seen that viewpoint 
  10. expressed many times.  Sympathetically, I agree in principle 
  11. with the emotional flavor of this statement, I suppose, but 
  12. when I am working on a program, I always reach a point at 
  13. which I can conclude that its performance is satisfactory.  
  14. At that stage I say that the program is finished.
  15.      The reason that some programmers, and perhaps some 
  16. users also, come to accept the never finished concept as 
  17. gospel is that they have seen too many programs, either 
  18. purchased or written, that never seem to perform completely 
  19. satisfactorily, and, therefore, seem to continuously require 
  20. fine tuning or corrections.  But this program attribute is 
  21. not an inherent consequence of program development.  The 
  22. problem with such programs is that their performance was 
  23. judged to be satisfactory prematurely.  Too often, the 
  24. performance of a program is judged to be satisfactory by its 
  25. author if the program seems to accomplish its primary 
  26. function after a few cursory tests.  Program testing, like 
  27. program documentation, seems to be a distasteful chore to 
  28. many programmers.  That's probably why so many programs are 
  29. thrust into the software market prematurely.
  30.      The attitude that I have developed is one which views 
  31. algorithmic design, documentation and testing as steps in a 
  32. single process, each of which demands the same level of 
  33. concentration, concern and quality control.  If you can 
  34. adopt a similar attitude, I guarantee that you will be a 
  35. happier, more successful programmer than one who finds any 
  36. phase of program development boring or distasteful.
  37.      Documentation is your front line defense against 
  38. programming catastrophes.  To be able to fix a program, at 
  39. repair time, you must be able to understand it as well as 
  40. you did when you wrote it.  The same level of understanding 
  41. is required when you decide to intentionally enhance a 
  42. program.  If program documentation includes the results of 
  43. performance testing, then a program's prior performance can 
  44. be used to gauge performance after alterations.
  45.      When a program seems to be malfunctioning, the first 
  46. action you take should be to compare its current performance 
  47. to past performance under known conditions.  Many times, 
  48. such comparisons will reveal that the execution environment, 
  49. not the program, is at fault.  Of course, it is then that 
  50. you may decide that a new version of the program is required 
  51. to cope with an altered execution environment.
  52.  
  53. The Three For One Theory
  54.   
  55.      When I was working on a large mainframe, the 
  56. manufacturer to remain nameless, for a company that shall 
  57. also remain nameless, we programmers developed a formula for 
  58. bug introduction into the mainframe's operating system.  For 
  59. every bug fixed, three more were introduced.  This is, of 
  60. course, one of Murphy's laws.  Sometimes the new bugs were 
  61. called enhancements to obscure the fact that they were 
  62. screwups.
  63.      But that's what all bugs are.  They are errors that you 
  64. make when you write your programs.  This is the first truth 
  65. that you should hold to be self-evident, if you want to 
  66. develop programs that eventually perform satisfactory.  Once 
  67. you realize that errors in your programs will be there 
  68. because of your own carelessness, or in spite of your best 
  69. efforts, you can take steps to prevent them from being 
  70. catastrophic.
  71.  
  72. Realistic Expectations
  73.   
  74.      When I judge the performance of an item of software or 
  75. hardware that I have purchased, I compare its performance to 
  76. the levels at which I have been led to believe it should be 
  77. according to the product's designer, manufacturer and 
  78. seller.  To that extent, if the product fails to meet my 
  79. expectations, then I have been cheated.  If I am fooled 
  80. twice by the same designer, manufacturer or seller, then I 
  81. have cheated myself.
  82.      When I judge the performance of an item of software or 
  83. hardware that I have designed and constructed,  I restrict 
  84. my expectations to levels that are commensurable with my 
  85. knowledge, experience and available tools.  If I have done 
  86. my best, then I can do no more, unless I decide to redesign 
  87. and reconstruct after obtaining more knowledge, more 
  88. experience or better tools.
  89.      The performance of an item of software is inherently 
  90. restricted by the design of the computer system--that should 
  91. be obvious.  Performance is also influenced by the extent of 
  92. the programmer's knowledge about the system and developed 
  93. programming ability.  A programmer's ability is developed 
  94. via education and experience.  To the extent that this 
  95. ability restricts the performance of final product, it is a 
  96. constituent of the overall programming environment.
  97.      If a program's performance depends on your programming 
  98. ability, how can you determine when its performance is 
  99. satisfactory?  Well, when a program executes a task 
  100. according to your specifications, then its performance must 
  101. be judged satisfactory.  How stringent should your 
  102. specifications be?  Your performance demands must be 
  103. commensurable with your programming ability.  When you have 
  104. exerted your best effort, you must be satisfied with the 
  105. final product; or you must obtain another system; or you 
  106. must accumulate more knowledge and experience, so that you 
  107. can demand more stringent specifications.
  108.      Accumulating Knowledge about your computer system's 
  109. capabilities can be a horrendous task: cataloging your 
  110. system's real, versus advertised or reported, capabilities 
  111. requires extensive performance testing of the system, 
  112. because you can't trust someone else's assessments.  For 
  113. example, on page 18 of my star NX-10 user's manual there is 
  114. a description of a self-test and the following statements:
  115.  
  116.   Were you surprised? It's fast, isn't it? About 120 
  117. characters a second, to be exact.
  118.   
  119.      Would any serious person use the words about and exact 
  120. in the way they are used above?  When I execute the test on 
  121. my printer, 503 characters are printed.  The elapsed time 
  122. according to my stopwatch was 6 seconds.  This means that 
  123. the printer prints 83 characters per second in that test 
  124. mode.  Even allowing for a one second error in timing, the 
  125. printing speed would be increased to only 100 characters per 
  126. second.  In order for the printer to meet specifications, it 
  127. would have to print the 503 characters in 4.19 seconds.
  128.      Again, on page 213 of the manual the printing speed in 
  129. Draft pica mode is specified to be 120 characters per 
  130. second; no about qualifier there.  When printing an ASCII 
  131. file in that mode, with the entire file contained in the 
  132. printer's buffer, so that the printer's speed depends only 
  133. upon its own capability, I measure a maximum printing speed 
  134. of 68 characters per second.
  135.      Are the manufacturer's specifications incorrect?  Is my 
  136. method of timing the printer's speed incorrect?  I have 
  137. learned not to be absolutely sure about anything in this 
  138. world, but I think my time would be wasted if I were to 
  139. spend it trying to develop a program which depended on the 
  140. printer's ability to print at 120 characters per second.  
  141. That would be an unrealistic expectation.
  142.  
  143. Performance Measuring Tools
  144.   
  145.      Because of its dependency on ability and personal 
  146. assessment, to some extent, performance testing must be 
  147. subjective.  In chapter 1, I said that I have been satisfied 
  148. with the star NX-10, and I have been, in spite of the 
  149. printing speed controversy.  The printer's other 
  150. capabilities and its low cost more than compensate for that 
  151. discrepancy, if it actually exists.  Therefore, in my 
  152. opinion, the performance of the printer is satisfactory.  
  153. This is my personal assessment.
  154.      Of course, one might be inclined to scold me, pointing 
  155. out that my method of measuring elapsed time during the 
  156. printing speed tests was crude.  To which I would reply, 
  157. "It's the only method that was available to me."  And, I 
  158. might add, I have found it to be much more reliable than 
  159. words printed on paper in a user's manual.  Any user's 
  160. manual.
  161.      One individual's judgement of the overall  performance 
  162. of a particular item of software is as subjective as is my 
  163. conclusions about the star NX-10.  But specific software 
  164. attributes can be judged objectively, if tools which can 
  165. measure pertinent aspects of performance are available.  I 
  166. am going to provide you with some of those tools in this 
  167. chapter.
  168.      I will introduce utility programs with which the 
  169. efficiency of individual instructions, algorithms and 
  170. programs may be compared.  I will provide programs that 
  171. perform many comparisons, but, since the subject of 
  172. performance testing must be restricted to a reasonable 
  173. length in the book, I will concentrate more on showing you, 
  174. by example, when and how I decide to conduct performance 
  175. tests, rather than flit through comparisons until you become 
  176. bored with the whole idea.
  177.  
  178. The First Utility
  179.   
  180.      While the primary objective of the chapter is to 
  181. provide performance measuring utilities, a secondary 
  182. objective is to illustrate specific stages of program 
  183. development.  I begin with the specifications for a utility 
  184. to be called SPEEDTST.  Then I introduce the first of a 
  185. series of programs, each of which is a model that represents 
  186. a snapshot of a continuous process.  The other programs 
  187. follow after the introduction of a program on which the 
  188. models can operate.
  189.      The programs introduced in this chapter invoke the 
  190. custom traps described in program 13.  To install the custom 
  191. traps, execute TRAPS.PRG.  If you want the traps to be 
  192. automatically installed during system boot, copy TRAPS.PRG 
  193. to the AUTO folder on your boot partition or floppy disk.
  194.      The first utility will calculate a program's load and 
  195. execution times.  As I concluded chapter 3, I mentioned that 
  196. the first stage of increasing a program's execution speed 
  197. involved getting it into ram as quickly as possible.  
  198. Methods of doing that will be discussed in this chapter.  In 
  199. order to discuss a variety of methods, I need a way to 
  200. measure the time required to load and execute a program.
  201.  
  202. Specifications For SPEEDTST
  203.   
  204.      SPEEDTST must accomplish the following:
  205.   
  206.      1. Spawn a process = load and execute a program.
  207.      2. Programs to be spawned will have a TOS or PRG         
  208.         suffix.
  209.      3. The spawned program will reside in the same         
  210.         directory as does SPEEDTST.
  211.      4. Create a disk file which is to be identified by 
  212.         the name of the spawned program with a DAT 
  213.         suffix.  The disk file is to reside in the same 
  214.         directory as does SPEEDTST.
  215.      5. Calculate the spawned program's load and         
  216.         execution times.
  217.      6. Store the load and execution times in the disk 
  218.         file described in item 4.
  219.      7. If the spawned process directs output to the 
  220.         video screen via GEMDOS function $9, redirect 
  221.         that output to the file described in item 4.
  222.         
  223. The First Model
  224.         
  225.      Program 15 is the first in a series of four programs 
  226. which progress in algorithmic perfection until the program 
  227. SPEEDTST is developed.  SPEED_1 is the first working model 
  228. of a parent program which loads and executes a child 
  229. program.  The parent calculates the spawned program's load 
  230. and execution times, using information returned to the 
  231. parent when the child terminates.  The parent creates a disk 
  232. file and stores the calculated values therein.  If the child 
  233. directs output to the screen using GEMDOS function $9, that 
  234. output will be redirected to the file.  The name of the file 
  235. created by the parent is composed of the name of the child, 
  236. without suffix, plus the extension DAT.
  237.      While it is doing all of that, the parent also confirms 
  238. that trap #6 has been installed by TRAPS.PRG and functions 
  239. correctly.  The parent accomplishes the verification simply 
  240. by being able to spawn, which it can't do if custom trap #6 
  241. fails to return excess memory to the operating system.  Trap 
  242. #6 also performs another function, but its effectiveness is 
  243. confirmed only if the child terminates using custom trap #8.  
  244. Refer to the extensive note in the data section of program 
  245. 16.
  246.     Program 16 must be assembled in PC-relative mode and the 
  247. executable file must be saved with a TTP extension.  When it 
  248. is executed, the filename of the program to be spawned must 
  249. reside in the same directory as does program 16.  Type the 
  250. name of the program to be spawned on program 16's input 
  251. parameter line.  As you shall see, a program that is to be 
  252. spawned by program 16 must be specifically prepared for the 
  253. spawning operation.
  254.      Program 16, as does programs 18 and 19, invokes custom 
  255. traps which must be installed by programs TRAPS.PRG (program 
  256. 13, chapter 4) and TRAP_9.PRG (program 15, chapter 5), 
  257. therefore, these programs must be executed from the desktop 
  258. or from the AUTO folder of a boot partition or floppy before 
  259. programs SPEED_1.TTP, SPEED_2.TTP or SPEED_3.TTP are 
  260. executed.  TRAP_9.S follows.
  261.  
  262.  
  263. Program 15. This program installs a custom trap for programs 
  264. 16, 18 and 19.
  265.  
  266.  ; Program Name: TRAP_9.S
  267.  ; Version 1.002
  268.  
  269.  ; Assembly Instructions:
  270.  
  271.  ;    Assemble in PC-relative mode and save with a PRG extension.
  272.  
  273.  ; Program Function:
  274.  
  275.  ;    This is a LSR program that establishes a user defined trap.  It may be
  276.  ; executed from the desktop, but you may prefer to copy it to the AUTO
  277.  ; folder of your boot partition or floppy disk so that it will execute
  278.  ; automatically during boot.
  279.  
  280.  ; MAJOR NOTE: SEE FURTHER DOCUMENTATION FOR THIS PROGRAM IN TRAPS.S.
  281.  
  282.  ;    Trap #9 is special in that it is only used by three programs: SPEED_1.TTP,
  283.  ; SPEED_2.TTP and SPEED_3.TTP.  The custom trap is used simply to reduce the
  284.  ; size of those programs.
  285.  
  286.  ;    This program invokes a custom trap that is established by TRAPS.PRG,
  287.  ; therefore, that program must be executed before trap #9 is invoked by a
  288.  ; program.
  289.  
  290. program_start:                  ; Calculate program size and retain result.
  291.  lea        program_end, a3     ; Fetch program end address.
  292.  suba.l     4(a7), a3           ; Subtract basepage address.
  293.  
  294. enter_supervisor_mode:
  295.  move.l     #0, -(sp)           ; The zero turns on supervisor mode.
  296.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  297.  trap       #1                  ; Go to supervisor mode.
  298.  addq.l     #6, sp              ; Supervisor stack pointer (SSP) returned in D0.
  299.  movea.l    d0, a5              ; Save SSP in scratch register.
  300.  
  301. install_trap_9_routine:         ; Note: pointer = vector = pointer.
  302.  lea        trap_9_routine, a0  ; Fetch address of trap #9 routine.
  303.  move.l     a0, $A4             ; Store custom trap address in pointer.
  304.  
  305. enter_user_mode:
  306.  pea        (a5)                ; Restore supervisor stack pointer.
  307.  move.w     #$20, -(sp)         ; Function = super = GEMDOS $20.
  308.  trap       #1                  ; Go to user mode.
  309.  addq.l     #6, sp              ; Reset stack pointer to top of stack. 
  310.  
  311. relinquish_processor_control:   ; Maintain memory residency.
  312.  move.w    #0, -(sp)            ; See page 121 of Internals book.
  313.  move.l    a3, -(sp)            ; Program size.
  314.  move.w    #$31, -(sp)          ; Function = ptermres = GEMDOS $31.
  315.  trap      #1
  316.  
  317. trap_9_routine:
  318.  
  319.  ; Expects a programs load time in register D3 as a binary number.  This
  320.  ; algorithm converts the value in D3 to milliseconds (msec) then prints the
  321.  ; load time in decimal msec.
  322.  
  323.  ; Also expects a programs execution time in register D5. The same service
  324.  ; is performed for the value in that register.
  325.  
  326. convert_load_time_to_msec:
  327.  move.l     d3, d0              ; Save a copy to add.
  328.  asl.l      #2, d3              ; Shift to multiply by 4.
  329.  add.l      d0, d3              ; To complete multiplication by 5.
  330.  
  331. print_load_time:
  332.  cmpi.l     #999, d3            ; If load time is less than 1000, then
  333.  bgt        no_space            ; print a leading blank space for output
  334.  lea        space, a0           ; alignment.
  335.  bsr        print_string
  336.  cmpi.l     #99, d3             ; If load time is less than 100, then
  337.  bgt        no_space            ; print another leading blank space.
  338.  lea        space, a0
  339.  bsr        print_string
  340. no_space:
  341.  move.l     d3, d1              ; Copy load time to D1 for decimal conversion.       
  342.  trap       #4                  ; Returns address of decimal string in A0.
  343.  bsr.s      print_string
  344.  lea        units_label, a0
  345.  bsr.s      print_string
  346.  
  347. convert_execution_time_to_msec: 
  348.  lea        execute_time_msg, a0
  349.  bsr.s      print_string        
  350.  move.l     d5, d0              ; Save a copy to add.
  351.  asl.l      #2, d5              ; Shift to multiply by 4.
  352.  add.l      d0, d5              ; To complete multiplication by 5.
  353.  
  354. print_execution_time:
  355.  cmpi.l     #999, d5            ; If execute time is less than 1000, then
  356.  bgt        _no_space           ; print a leading blank space for output
  357.  lea        space, a0           ; alignment.
  358.  bsr        print_string
  359.  cmpi.l     #99, d5             ; If execute time is less than 100, then
  360.  bgt.s      _no_space           ; print another leading blank space.
  361.  lea        space, a0
  362.  bsr.s      print_string
  363. _no_space:
  364.  move.l     d5, d1              ; Copy execute time for decimal conversion.
  365.  trap       #4                  ; Returns address of decimal string in A0.
  366.  bsr.s      print_string
  367.  lea        units_label, a0
  368.  bsr.s      print_string
  369.  rte
  370.  
  371.  ;
  372.  ; Subroutine
  373.  ;
  374.  
  375. print_string:                   ; Expects address of string to be in A0.
  376.  pea        (a0)                ; Push address of string onto stack.
  377.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  378.  trap       #1                  ; GEMDOS call
  379.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  380.  rts
  381.  
  382.  data
  383. space:            dc.b   " ",0
  384. execute_time_msg: dc.b         "  Execute time: ",0
  385. units_label:      dc.b   "  milliseconds", $D,$A,0
  386.  bss
  387.  align                       ; Align storage on a word boundary.
  388. program_end:      ds.l   0
  389.  end
  390.   
  391.  
  392. Program 16. A utility that computes a program's load and 
  393. execution times.
  394.  
  395.  ; Program Name: SPEED_1.S
  396.  ; Version: 1.006        
  397.  
  398.  ; Assembly Instructions:
  399.  
  400.  ;     Assemble in "PC-relative" mode and save with a TTP extension. 
  401.  
  402.  ; Execution Instructions:
  403.  
  404.  ;     SPEED_1.TTP will not execute unless the custom traps in program
  405.  ; TRAPS.PRG and TRAP_9.PRG have previously been installed.  The custom
  406.  ; traps are installed when those programs are executed from the desktop
  407.  ; or from an AUTO folder on a boot disk.
  408.  
  409.  ; NOTE: The time required for a program to be loaded into memory depends
  410.  ;       on the assembly mode used to assemble the program.  This will be
  411.  ;       shown, using SPEEDTST.TTP, in chapter 5.
  412.  
  413.  ;       In addition, a program's load time depends on the drive from which
  414.  ;       the program is loaded, the method used to format the disk on which
  415.  ;       the program is located, the position of the program on the disk
  416.  ;       and, in this case, the position of the child process relative to the
  417.  ;       position of the parent process.
  418.  
  419.  ;       To eliminate the drive-variables when comparing the load and
  420.  ;       execution times of one program to that of another, the parent and
  421.  ;       the child should be isolated to an otherwise empty partition or
  422.  ;       floppy disk for each spawning instance.
  423.  
  424.  ;       For example, if there are two programs involved in the comparison,
  425.  ;       first copy the parent, which is SPEED_1 in this case, so that it is
  426.  ;       the only item in the hard disk partition or on the floppy.  Then,
  427.  ;       copy the first program to the same partition or floppy.  Execute
  428.  ;       the parent, SPEED_1 in this case, and obtain the results.
  429.  
  430.  ;       Remove the first program and copy the second.  Execute the parent
  431.  ;       and obtain the results for the second program.
  432.  
  433.  ;     Execute from the desktop.  Type the name of an executable file which
  434.  ; has a TOS or PRG extension on SPEED_1.TTP's input parameter line.  The
  435.  ; name of the program you type on the parameter line must be in the same
  436.  ; directory as is SPEED_1.TTP and the program must be one that terminates
  437.  ; with GEMDOS function $4C.
  438.  
  439.  ;     Upon termination, the spawned program must return the value that is in
  440.  ; memory location $4BA immediately after it has been loaded, hereafter called
  441.  ; the after-load time or after-load value.  Custom trap #3 (get_time) can be
  442.  ; used to obtain that value.  SPEED_1.TTP uses the value returned in D0 to
  443.  ; calculate the spawned program's load and execution times.
  444.  
  445.  ;     The spawned program must terminate with GEMDOS function $4C so that
  446.  ; the after-load value can be returned in D0 by that function.  The value
  447.  ; returned in D0 by GEMDOS function $4C is limited to a 16 bit value.
  448.  
  449.  ;     If the spawned program has any halt or wait instructions, such as wait
  450.  ; for a keypress, etc., those should be commented out, then the program
  451.  ; should be assembled especially for the speed test.  Otherwise the
  452.  ; execution time will include the time waiting for input.
  453.  
  454.  ;     If custom trap #8 is used to terminate the program, the trap will
  455.  ; execute a wait_for_keypress algorithm when the program is executed from
  456.  ; the desktop, but it will omit the wait algorithm when the program is
  457.  ; spawned by SPEED_1.TTP.  In addition, trap #8 will return the after-load
  458.  ; value to SPEED_1.TTP and terminate the spawned program with GEMDOS function
  459.  ; $4C.
  460.  
  461.  ;     Both trap #8 and SPEED_1.TTP require that the spawned program be
  462.  ; initialized with custom trap #6.  See the note in the data section, below.
  463.  
  464.  ; Primary Function:
  465.  
  466.  ;     Spawn a process.  Calculate the spawned program's load and execution
  467.  ; times.  Store these values in a disk file that is identified by the name
  468.  ; of the spawned process with a DAT suffix.
  469.  
  470.  ;     If the spawned process directs output to the screen, store that output
  471.  ; in the same disk file.  Note: only screen directed output processed by
  472.  ; GEMDOS function $9 will be directed to the file.  If BIOS function $3 is
  473.  ; used for screen output, that output will not be redirected to the file.
  474.  
  475.  ; Secondary Function:
  476.  
  477.  ;     Verify that trap #6 is resident and functions correctly.  SPEED_1
  478.  ; confirms that because it will not be able to spawn a process unless 
  479.  ; the trap #6 call has returned excess memory to the system.
  480.   
  481.  ; Description:
  482.  
  483.  ;     SPEED_1 is the first in a series of programs which progress in
  484.  ; algorithmic perfection until the program SPEEDTST is developed.  Using
  485.  ; this series of programs, I intend to help you experience selected stages
  486.  ; of a program development process.
  487.  
  488.  ;     The primary attribute of this development process is its dependence,
  489.  ; during the early stages of development, on familiar documented algorithms
  490.  ; that can easily be found in references for many programming languages.  
  491.  ; After a working model has been developed with these familiar algorithms,
  492.  ; attempts are made to introduce unfamiliar algorithms which may be faster
  493.  ; or consume less memory.
  494.  
  495. release_excess_memory:
  496.  lea        program_end, a0     ; Put "end of program" address in A0.
  497.  movea.l    4(a7), a1           ; Put "basepage" address in A1.
  498.  movea.l    a1, a4              ; Copy to A4 for command line access.
  499.  trap       #6                  ; Calculate program size and release memory.
  500.  
  501.  ; NOTE: A local stack is not declared in PRG_5AP.TOS.  Because of the long
  502.  ;       string that is printed by that program, this program will bomb when
  503.  ;       it spawns PRG_5AP.TOS, if a local stack is not declared here.
  504.  
  505.  lea        stack, a7           ; Point A7 to this program's stack.
  506.  
  507.  ; The next task to be accomplished is an initialization algorithm.  The
  508.  ; name of the program that is to be typed on SPEED_1.TTP's input parameter
  509.  ; line must be used in several ways.  First, its suffix must be changed to
  510.  ; DAT so that it can be passed as a NULL terminated string when GEMDOS $3C
  511.  ; is invoked to create the disk file.
  512.  
  513.  ; Then it must be passed as a NULL terminated string with the program's
  514.  ; original suffix when GEMDOS $4B is invoked to spawn the program.
  515.  
  516.  ; Finally, the program's name is used as part of SPEED_1.TTP's output
  517.  ; header.
  518.  
  519.  ; The command line processing algorithm creates the required NULL terminated
  520.  ; strings, storing them in locations declared in the data section of SPEED_1.
  521.  
  522. process_command_line_parameters:
  523.  lea        $80(a4), a4         ; Fetch address of parameters.
  524.  move.b     (a4)+, d0           ; Fetch parameter line character count.
  525.  lea        program_name, a3    ; Load program_name address in A3.
  526.  subq.b     #1, d0              ; Set up counter.
  527.  ext.w      d0                  ; Extend to match the size of the dbra
  528.                                 ; instruction.
  529.  
  530.  ; NOTE: The dbcc instruction operates on a word length value, therefore,
  531.  ;       the value in the register that is to be decremented by a dbcc
  532.  ;       instruction must be placed there with a word size instruction, such
  533.  ;       as move.w #10, D0; or with a longword size instruction, as long as
  534.  ;       the value in the longword is limited to word size validity, or with
  535.  ;       a byte size instruction, as long as the value in the register is
  536.  ;       sign extended to word size, as is done in the instruction above.
  537.       
  538. fetch_character:                
  539.  move.b     (a4)+, (a3)+        ; Store character.
  540.  dbra       d0, fetch_character ; Loop until d0 becomes negative.
  541.  move.b     #0, (a3)            ; Finish with a NULL.
  542.  
  543. create_file_name:               ; Create a file to accept standard output.
  544.  lea        filename, a4
  545.  lea        program_name, a3
  546. copy_name:
  547.  move.b     (a3)+, (a4)+
  548.  cmpi.b     #$2E, (a3)          ; Is next byte of program_name the period?
  549.  bne.s      copy_name           ; Continue looping until period is seen.
  550.  move.b     #$2E, (a4)+         ; Add a period.
  551.  move.b     #$44, (a4)+         ; Add letter 'D'.
  552.  move.b     #$41, (a4)+         ; Add letter 'A'.
  553.  move.b     #$54, (a4)+         ; Add letter 'T'.
  554.  move.b     #0, (a4)            ; Add a NULL.
  555.  
  556. create_file:
  557.  move.w     #0, -(sp)           ; File attribute = read/write.
  558.  pea        filename            ; Will be name of spawned process + .DAT.
  559.  move.w     #$3C, -(sp)         ; Function = f_create = GEMDOS $3C.
  560.  trap       #1                  ; File handle is returned in D0.
  561.  addq.l     #8, sp
  562.  lea        file_handle, a0     ; Store returned file handle.
  563.  move.w     d0, (a0)
  564.  
  565. redirect_output:                ; Exchange file handle with screen's handle.
  566.  move.w     file_handle, -(sp)  ; This is the disk file's handle.
  567.  move.w     #1, -(sp)           ; This is the video screen's handle.
  568.  move.w     #$46, -(sp)         ; Function = f_force = GEMDOS $46.
  569.  trap       #1
  570.  addq.l     #6, sp
  571.  
  572. get_start_time:
  573.  lea        start_time, a3      ; Fetch address of variable "start_time".
  574.  trap       #3                  ; Returns value of system clock in D0.
  575.  move.w     d0, (a3)            ; Save start time.
  576.  
  577. load_and_execute_program:
  578.  pea        environ_string
  579.  pea        command_line
  580.  pea        program_name
  581.  move.w     #0, -(sp)
  582.  move.w     #$4B, -(sp)         ; Function = GEMDOS $4B = p_exec.
  583.  trap       #1                 
  584.  move.w     d0, d3              ; Copy after-load value to D3 for calculation.
  585.  
  586. get_end_time:
  587.  trap       #3                  ; Returns value of system clock in D0.
  588.  move.w     d0, d5              ; Copy to D5 for calculation.
  589.  sub.w      d3, d5              ; Subtract after-load time from end time.
  590.  ext.l      d5                  ; Extend to 32 bits.
  591.  
  592.  ; NOTE: D5 now contains the spawned program's execution time, but the time
  593.  ;       has not yet been converted to milliseconds.  See the note below
  594.  ;       concerning the sign extension of D3 and D5.
  595.  
  596. reposition_stack_pointer:
  597.  lea        $10(sp), sp        
  598.  
  599.  ;       Note the difference between the use of GEMDOS function $19 below and
  600.  ;       the way it is used on page 116 of the Internals book.  In the 
  601.  ;       Internals book there are two errors: (1) sp should not be referenced
  602.  ;       indirectly, as (sp); (2) the ASCII code for the letter A should be
  603.  ;       added to the contents of the register--in the internals book the
  604.  ;       contents of the register are added to the ASCII code for the letter
  605.  ;       A.
  606.  
  607. get_drive:
  608.  move.w     #$19, -(sp)         ; Function = dgetdrv = GEMDOS $19.
  609.  trap       #1                  ; Returns 0 for drive A, 1 for B, etc.
  610.  addq.l     #2, sp              
  611.  add.b      #$41, d0            ; Add ASCII value for A to compute ASCII
  612.  lea        drive, a0           ; letter code for the drive value returned.
  613.  move.b     d0, (a0)            ; Save drive's ASCII letter code.
  614.  
  615. print_heading:
  616.  lea        heading, a0
  617.  bsr        print_string
  618.  lea        program_name, a0
  619.  bsr        print_string
  620. print_drive_for_spawned_program:
  621.  lea        drive_msg, a0
  622.  bsr        print_string
  623.  
  624. compute_load_time:
  625.  lea        load_time_msg, a0
  626.  bsr.s      print_string
  627.  lea        start_time, a3
  628.  sub.w      (a3), d3            ; Subtract start time from after-load time.
  629.  ext.l      d3                  ; Extent to 32 bits.
  630.  
  631.  ; SIGN EXTENSION NOTE
  632.  
  633.  ;    The value in D3, above, and in D5 previously, is extended to 32 bits
  634.  ; because, although the number of 200hz intervals we are able to utilize is
  635.  ; limited to a word size by the value that is returned in D0 via GEMDOS
  636.  ; function $4C, the time converted to milliseconds can extend beyond that
  637.  ; word size limitation.
  638.  
  639.  trap       #9                  ; See description in TRAP_9.S.
  640.  
  641. close_file:
  642.  move.w     file_handle, -(sp)
  643.  move.w     #$3E, -(sp)         ; Function = fclose = GEMDOS $3E.
  644.  trap       #1
  645.  addq.l     #4, sp
  646.  
  647. terminate:
  648.  move.w     #0, -(sp)
  649.  trap       #1
  650.  
  651. print_string:                   ; Expects address of string to be in A0.
  652.  pea        (a0)                ; Push address of string onto stack.
  653.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  654.  trap       #1                  ; GEMDOS call
  655.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  656.  rts
  657.  
  658.  data
  659. heading:          dc.b $D,$A,"SPEED_1.TTP Execution Results",$D,$A
  660.                   dc.b       "for ",0
  661. drive_msg:        dc.b ", loaded from drive: "
  662. drive:            dc.b "A",$D,$A,0
  663. load_time_msg:    dc.b $D,$A,"  Load time:    ",0
  664.  
  665.  ; NOTE: Custom trap #6 checks the environmental string pointer of each
  666.  ;       program that invokes it to see if the pointer contains the address
  667.  ;       of the label "environ_string" below.  That test is performed by
  668.  ;       comparing the contents of the address contained in the pointer to
  669.  ;       the ASCII string "TERM" declared below.
  670.  
  671.  ;       When a match occurs, it means that the program invoking trap #6 has
  672.  ;       been spawned by SPEED_1 (or by a similar program), therefore, trap
  673.  ;       #6 sets the value of the boolean variable "spawned", declared by
  674.  ;       TRAPS.PRG, to all ones = true.
  675.  
  676.  ;       When custom trap #8 is invoked by a program, the state of the
  677.  ;       variable "spawned" is tested.  If the state is true, the program
  678.  ;       invoking custom trap #8 is terminated with GEMDOS function $4C and
  679.  ;       the after-load time, which was saved by custom trap #6, is returned
  680.  ;       to the parent program.
  681.  
  682.  ;       If the state of "spawned" is false, GEMDOS function $8 is executed
  683.  ;       so that execution will pause for a keypress.  When the keypress is
  684.  ;       received, GEMDOS function $0 is executed.
  685.  
  686.  ;       In this manner, custom trap #8, working in conjunction with custom
  687.  ;       trap #6, eliminates the "wait for keypress" algorithm automatically
  688.  ;       when a program is spawned by SPEED_1 (or a similar program).  This
  689.  ;       prevents the computed execution time from being corrupted by a time
  690.  ;       period that involves a wait for keyboard input.
  691.   
  692. environ_string:   dc.b "TERM",0
  693. command_line:     dc.b 0       
  694.  align
  695.  bss
  696. start_time:       ds.w    1     ; Value in $4BA just before spawning.
  697. file_handle:      ds.w    1     ; Handle for the filename below.
  698. filename:         ds.l    4     ; File name for execution results.
  699. program_name:     ds.l    4     ; Filename buffer.  Must be NULL terminated.
  700.                   ds.l   96     ; Program stack.
  701. stack:            ds.l    0     ; Address of program stack.
  702. program_end:      ds.l    0
  703.  end
  704.  
  705.  
  706.      Program 17 was prepared as a simple example to be 
  707. executed by program 16 and the other programs in the series.  
  708. Program 17 illustrates the use of custom traps #3, #6 and 
  709. #8.  Assemble programs 16 and 17, then, with their 
  710. executable files in the same directory, execute program 16.  
  711. Type the name of program 17's executable file on program 
  712. 16's command line.  Figure 5.1 shows the contents of the 
  713. file produced by program 16.  The values stored in the file 
  714. depend on the variables mentioned in program 16's 
  715. documentation.
  716.  
  717. Program 17. Execute this program by typing PRG_5AP.TOS on 
  718. SPEED_1.TTP's command line.
  719.  
  720.  ; Program Name: PRG_5AP.S
  721.  ; Version 1.003
  722.  
  723.  ; Assembly Instructions:
  724.  
  725.  ;    Assemble in PC-relative mode and save with a TOS extension.
  726.  
  727.  ; Execution Note:
  728.  
  729.  ;    This program invokes custom traps which must be installed by
  730.  ; TRAPS.PRG prior to its execution.
  731.  
  732.  ; Program Function:
  733.  
  734.  ;    This program illustrates the use of custom traps #3, #6 and #8.
  735.  ; If the program is executed from the desktop, trap #8 will execute the
  736.  ; wait_for_keypress algorithm, then, when a key is pressed it will execute
  737.  ; GEMDOS function 0.
  738.  
  739.  ;    If, instead, this program is executed by typing its name on
  740.  ; SPEEDTST.TTP's input parameter line, trap #8 will not execute the
  741.  ; wait_for_keypress algorithm, but it will immediately execute GEMDOS
  742.  ; function $4C.
  743.  
  744.  ;    Trap #3 returns, in D0, the value of the system clock as it is
  745.  ; immediately after this program has been loaded.  The value in D0 is not
  746.  ; corrupted before trap #6 is invoked, therefore, it is still valid when
  747.  ; the trap #6 routine begins to execute.  Trap #6 saves the "after-load"
  748.  ; value of the system clock in its own local variable, where it is available
  749.  ; for processing during the execution of trap #8.
  750.  
  751.  ;    Trap #6 also calculates the memory occupied by this program and releases
  752.  ; the memory not occupied by this program to the operating system.
  753.   
  754. fetch_load_time:              
  755.  trap       #3                  ; Returns value of system clock in D0.
  756. release_excess_memory:          ; Also stores after-load time in TRAPS bss.
  757.  lea        program_end, a0     ; Put "end of program" address in A0.
  758.  movea.l    4(a7), a1           ; Put "basepage" address in A1.
  759.  trap       #6                  ; Calculate program size and release memory.
  760.  
  761. waste_time:
  762.  move.l     #$1, d0
  763. outer_loop:
  764.  move.l     #$FDE8, d1
  765. inner_loop:
  766.  move.l     #$FDE8, d2
  767.  dbra       d1, inner_loop
  768.  dbra       d0, outer_loop
  769.  
  770.  lea        heading, a0
  771.  bsr.s      print_string
  772.  lea        string, a0
  773.  bsr.s      print_string
  774.  
  775.  trap       #8                  ; Terminate.
  776. print_string:
  777.  pea        (a0)
  778.  move.w     #9, -(sp)
  779.  trap       #1
  780.  addq.l     #6, sp
  781.  rts
  782.  
  783.  data
  784. heading:     dc.b 'PRG_5AP.TOS Execution Results',$D,$A,$D,$A,0
  785. string:      dc.b '  When executed from the desktop, this program will print '
  786.              dc.b 'this string on the',$D,$A
  787.              dc.b '  video screen and pause for a keypress.  But, when this '
  788.              dc.b 'program is spawned by',$D,$A
  789.              dc.b '  SPEED_1, SPEED_2, SPEED_3 or SPEEDTST, the string will '
  790.              dc.b 'be stored in a file ',$D,$A
  791.              dc.b '  named PRG_5AP.DAT and the program will not pause for a '
  792.              dc.b ' keypress.',$D,$A,0
  793.  bss
  794.  align
  795. program_end: ds.l 0
  796.  end                            ; Assembler pseudo-op.
  797.  
  798. PRG_5AP.TOS Execution Results
  799.  
  800.   When executed from the desktop, this program will print this string on the
  801.   video screen and pause for a keypress.  But, when this program is spawned by
  802.   SPEED_1, SPEED_2, SPEED_3 or SPEEDTST, the string will be stored in a file 
  803.   named PRG_5AP.DAT and the program will not pause for a  keypress.
  804.  
  805.   
  806. Figure 5.1. Contents of PRG_5AP.DAT, the data file produced 
  807. by program 16 to contain program 17's load and execution 
  808. times.
  809.  
  810. PRG_5AP.TOS Execution Results
  811.  
  812.   When executed from the desktop, this program will print this string on the
  813.   video screen and pause for a keypress.  But, when this program is spawned by
  814.   SPEED_1, SPEED_2, SPEED_3 or SPEEDTST, the string will be stored in a file 
  815.   named PRG_5AP.DAT and the program will not pause for a  keypress.
  816.  
  817. SPEED_1.TTP Execution Results
  818. for PRG_5AP.TOS, loaded from drive: G
  819.  
  820.   Load time:       45  milliseconds
  821.   Execute time:   680  milliseconds
  822.   
  823.  
  824. The Second Model
  825.   
  826.      After program 16 was operational, I began to think 
  827. about ways I might improve the command line processing 
  828. algorithm.  Also, I decided to try to improve the accuracy 
  829. of the calculated load time by initializing the stack for 
  830. GEMDOS $4B, withholding the invocation of trap #1, then 
  831. invoking trap #3 to get the start time, just before invoking 
  832. trap #1 to load and execute program 17.
  833.      The improvements are incorporated in program 18, the 
  834. next program in the series.  In SPEED_2, the movem.l 
  835. instruction is used to move the command line to four 
  836. registers, then from there to a declared location in the 
  837. data section.  Since this program is simply a model, and 
  838. since the algorithms which create the disk file were 
  839. developed in SPEED_1, I decided that there was no reason to 
  840. repeat those algorithms in SPEED_2.
  841.      However, I discovered that, for no apparent reason, the 
  842. load time reported by SPEED_2 increased significantly, even 
  843. though the experiments with SPEED_1 and SPEED_2 were 
  844. executed under identical conditions.  By eliminating each of 
  845. SPEED_1's algorithms that are involved with the disk file, 
  846. in turn, I learned that, for some reason, the load time is 
  847. shorter when a file is created.  Therefore, in order to 
  848. maintain a valid experiment, I created a dummy file in 
  849. SPEED_2, but wrote nothing to it.
  850.      But by the time I got to SPEEDTST, I realized that the 
  851. file name creation algorithm was actually a part of the 
  852. command line processing algorithm, therefore, in order to 
  853. validate comparisons between the three models, I had to redo 
  854. SPEED_2 and SPEED_3, including a file name creation 
  855. algorithm in each.  While doing that, I was able to use the 
  856. movem.l instruction to develop a faster creation algorithm 
  857. than that used in SPEED_1.
  858.  
  859. Program 18. The next stage of SPEEDTST.TTP's development.
  860.  
  861.  ; Program Name: SPEED_2.S
  862.  ; Version 1.003
  863.  
  864.  ; NOTE: This program is similar to SPEED_1.  The differences between the
  865.  ;       the two is that this one uses a different algorithm to process
  866.  ;       the command line, and it fetches the start time at a more appropriate
  867.  ;       place in the program.
  868.  
  869.  ; Assembly Instructions:
  870.  
  871.  ;     Assemble in "PC-relative" mode and save with a TTP extension. 
  872.  
  873.  ; Function:
  874.  
  875.  ;     Spawn a process and calculate the spawned program's load and execution
  876.  ; times.  Pause for a keypress before terminating.
  877.  
  878. release_excess_memory:
  879.  lea        program_end, a0     ; Put "end of program" address in A0.
  880.  movea.l    4(a7), a1           ; Put "basepage" address in A1.
  881.  movea.l    a1, a4              ; Copy to A4 for command line access.
  882.  trap       #6                  ; Calculate program size and release memory.
  883.  
  884.  ; NOTE: A local stack is not declared in PRG_5AP.TOS.  Because of the long
  885.  ;       string that is printed by that program, this program will bomb when
  886.  ;       it spawns PRG_5AP.TOS, if a local stack is not declared here.
  887.  
  888.  lea        stack, a7           ; Point A7 to this program's stack.
  889.  
  890.  ;           NOTE ABOUT THE COMMAND LINE PROCESSING ALGORITHM
  891.  
  892.  ;     Refer to figure 2.13 of chapter 2 for an image of a command line
  893.  ; that is stored in a program's basepage.  The first byte of the command
  894.  ; line is a count of the ASCII characters contained therein.  The second
  895.  ; byte is the first character in the command line.  The last character in
  896.  ; the command line is followed by the ASCII code for a carriage return; 
  897.  ; the carriage return is not included in the character count.
  898.  
  899.  ;     For program SPEED_2 we know that the command line character count
  900.  ; cannot exceed 12 characters = 12 bytes = 3 longwords.  Therefore, it
  901.  ; would be convenient if those 3 longwords could be transfered directly to
  902.  ; three data registers.  Unfortunately, the MC68000 will not permit the
  903.  ; movem instruction to transfer data which begins at an odd address.
  904.  
  905.  ;     Because of this restriction, it would be convenient if the operating
  906.  ; system stored the first command line character at an even address.
  907.  ; Unfortunately, it does not.  Therefore, we are forced to fetch 4 longwords
  908.  ; from the vicinity of the command line.  That's why we must use four data
  909.  ; registers instead of three.
  910.  
  911.  ;     To complicate things, the command line ASCII string will be corrupted
  912.  ; by the first byte in the first register, because it is the character count,
  913.  ; not a valid character.  So, when the data contained in the data registers
  914.  ; are transferred to a declared variable location, this byte must be stripped
  915.  ; from the command line ASCII string.
  916.  
  917.  ;     I accomplish this with no wasted time by declaring two variable
  918.  ; locations, input_line and program_name.  Since input_line is one byte in
  919.  ; length, and the first location for program_name immediately follows that
  920.  ; byte, when the contents of the data registers is moved to the location of
  921.  ; input_line, the variable program_name will point to the first character
  922.  ; of the command line ASCII string, as it should.
  923.  
  924.  ;     The carriage return at the end of the ASCII string is also transferred
  925.  ; to the 15 byte array addressed by program_name.  It must be overwritten by
  926.  ; a NULL so that the ASCII string is NULL terminated.  That is accomplished
  927.  ; fetching the command line character count as a byte length value, extending
  928.  ; it to word length and using the result in an operand that uses "address
  929.  ; register indirect with index" addressing.      
  930.   
  931. process_command_line:
  932.  lea        input_line, a3      ; Fetch location to contain command line.
  933.  lea        output_line, a5     ; A second location: for filename.
  934.  movem.l    $80(a4), d0-d3      ; Move 16 bytes of command line to 4 registers.
  935.  movem.l    d0-d3, (a3)         ; Move them to address "input_line".
  936.  movem.l    d0-d3, (a5)         ; Move them to address "output_line".
  937.  move.b     $80(a4), d0         ; Fetch command line ASCII character count.
  938.  ext.w      d0                  ; Extend to word for next instruction.
  939.  move.b     #0, 1(a3,d0.w)      ; Store a null at end of command line input.
  940.  move.b     #0, 1(a5,d0.w)      ; Same for filename buffer.
  941.  
  942. insert_filename_suffix:
  943.  move.b     #$44, -2(a5,d0.w)   ; Insert letter 'D'.
  944.  move.b     #$41, -1(a5,d0.w)   ; Insert letter 'A'.
  945.  move.b     #$54,  0(a5,d0.w)   ; Insert letter 'T'.
  946.  
  947. create_file:
  948.  move.w     #0, -(sp)           ; File attribute = read/write.
  949.  pea        filename            ; Will be name of spawned process + .DAT.
  950.  move.w     #$3C, -(sp)         ; Function = f_create = GEMDOS $3C.
  951.  trap       #1                  ; File handle is returned in D0.
  952.  addq.l     #8, sp
  953.  lea        file_handle, a0
  954.  move.w     d0, (a0)
  955.  
  956. redirect_output:                ; Exchange file handle with screen's handle.
  957.  move.w     file_handle, -(sp)  ; This is the disk file's handle.
  958.  move.w     #1, -(sp)           ; This is the video screen's handle.
  959.  move.w     #$46, -(sp)         ; Function = f_force = GEMDOS $46.
  960.  trap       #1
  961.  addq.l     #6, sp
  962.  
  963.  ; NOTE: In order to increase the accuracy of the start time, the stack is
  964.  ;       prepared for the spawning process, then, just before trap #1 is
  965.  ;       invoked, custom trap #3 is invoked and the start time is saved.
  966.  
  967. prepare_stack_for_load_and_execute_program:      
  968.  pea        environ_string
  969.  pea        command_line
  970.  pea        program_name
  971.  move.w     #0, -(sp)
  972.  move.w     #$4B, -(sp)         ; Function = GEMDOS $4B = p_exec.
  973.  
  974. get_start_time:
  975.  lea        start_time, a3      ; Fetch address of variable "start_time".
  976.  trap       #3                  ; Returns value of system clock in D0.
  977.  move.w     d0, (a3)            ; Save start time.
  978. load_and_execute_program:
  979.  trap       #1                 
  980.  move.w     d0, d3              ; Copy after-load value to D3 for calculation.
  981.  
  982. get_end_time:
  983.  trap       #3                  ; Returns value of system clock in D0.
  984.  move.w     d0, d5              ; Copy to D5 for calculation.
  985.  sub.w      d3, d5              ; Subtract after-load time from end time.
  986.  ext.l      d5                  ; Extend to 32 bits.
  987.  
  988. reposition_stack_pointer:
  989.  lea        $10(sp), sp        
  990.  
  991. get_drive:
  992.  move.w     #$19, -(sp)         ; Function = dgetdrv = GEMDOS $19.
  993.  trap       #1
  994.  addq.l     #2, sp
  995.  add.b      #'A', d0
  996.  lea        drive, a0
  997.  move.b     d0, (a0)
  998.  
  999. print_heading:
  1000.  lea        heading, a0
  1001.  bsr        print_string
  1002.  lea        program_name, a0
  1003.  bsr        print_string
  1004. print_drive_for_spawned_program:
  1005.  lea        drive_msg, a0
  1006.  bsr        print_string
  1007.  
  1008. compute_load_time:
  1009.  lea        load_time_msg, a0
  1010.  bsr        print_string
  1011.  lea        start_time, a3
  1012.  sub.w      (a3), d3            ; Subtract start time from after-load time.
  1013.  ext.l      d3                  ; Extent to 32 bits.
  1014.  trap       #9                  ; See description in TRAPS.S.
  1015.  
  1016. close_file:
  1017.  move.w     file_handle, -(sp)
  1018.  move.w     #$3E, -(sp)         ; Function = fclose = GEMDOS $3E.
  1019.  trap       #1
  1020.  addq.l     #4, sp
  1021.  
  1022. terminate:
  1023.  move.w     #0, -(sp)
  1024.  trap       #1
  1025.  
  1026. print_string:                   ; Expects address of string to be in A0.
  1027.  move.l     a0, -(sp)           ; Push address of string onto stack.
  1028.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  1029.  trap       #1                  ; GEMDOS call
  1030.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  1031.  rts
  1032.  
  1033.  data
  1034. heading:          dc.b $D,$A,"SPEED_2.TTP Execution Results",$D,$A
  1035.                   dc.b       "for ",0
  1036. drive_msg:        dc.b ", loaded from drive: "
  1037. drive:            dc.b "A",$D,$A,0
  1038. load_time_msg:    dc.b $D,$A,"  Load time:    ",0
  1039. environ_string:   dc.b "TERM",0
  1040. command_line:     dc.b 0
  1041.  align
  1042.  bss
  1043. start_time:       ds.w    1
  1044. file_handle:      ds.w    1
  1045. input_line:       ds.b    1
  1046. program_name:     ds.b   15     ; Program name buffer.
  1047. output_line:      ds.b    1 
  1048. filename:         ds.b   15     ; Filename buffer.
  1049.                   ds.l   96     ; Program stack.
  1050. stack:            ds.l    0     ; Address of program stack.
  1051. program_end:      ds.l    0
  1052.  end
  1053.  
  1054.  
  1055. SPEED_2.TTP Execution Results
  1056.  
  1057. PRG_5AP.TOS Execution Results
  1058.  
  1059.   When executed from the desktop, this program will print this string on the
  1060.   video screen and pause for a keypress.  But, when this program is spawned by
  1061.   SPEED_1, SPEED_2, SPEED_3 or SPEEDTST, the string will be stored in a file 
  1062.   named PRG_5AP.DAT and the program will not pause for a  keypress.
  1063.  
  1064. SPEED_2.TTP Execution Results
  1065. for PRG_5AP.TOS, loaded from drive: G
  1066.  
  1067.   Load time:       45  milliseconds
  1068.   Execute time:   680  milliseconds
  1069.  
  1070.  
  1071.      Program 19 serves as the final model to be considered 
  1072. in the development of SPEEDTST.TTP.  Within it, a new 
  1073. command line processing algorithm is developed and the user 
  1074. declared stack is discarded.  As in the two previous models, 
  1075. the command line algorithm must prepare two strings: one to 
  1076. be used as the name of a disk file, the other to be passed 
  1077. as a parameter when GEMDOS $4B is invoked.  The latter 
  1078. string is also used as part of the utility's header.  The 
  1079. algorithm in this model takes advantage of the presence of 
  1080. the string in the command line to eliminate movement to 
  1081. prepare the latter string.  Instead, the string is altered 
  1082. in place, at its location in the basepage.  The movement 
  1083. which is required is a prerequisite for preparation of the 
  1084. filename string.
  1085.  
  1086.  
  1087. Program 19. The final program model in SPEEDTST.TTP's 
  1088. development.
  1089.  
  1090.  ; Program Name: SPEED_3.S
  1091.  ; Version 1.004
  1092.  
  1093.  ; NOTE: This program is similar to SPEED_2.  The differences are that a
  1094.  ;       different algorithm is used to process the command line, and no user
  1095.  ;       stack is declared in SPEED_3.
  1096.  
  1097.  ; Assembly Instructions:
  1098.  
  1099.  ;     Assemble in "PC-relative" mode and save with a TTP extension. 
  1100.  
  1101.  ; Function:
  1102.  
  1103.  ;     Spawn a process and calculate the spawned program's load and execution
  1104.  ; times.  Pause for a keypress before terminating.
  1105.  
  1106. release_excess_memory:
  1107.  lea        program_end, a0     ; Put "end of program" address in A0.
  1108.  movea.l    4(a7), a1           ; Put "basepage" address in A1.
  1109.  lea        $80(a1), a3         ; Put "command line" address in A3.
  1110.  trap       #6                  ; Calculate program size and release memory.
  1111.  
  1112.  ; NOTE: A local stack is not declared in PRG_5AP.TOS.  Because of the long
  1113.  ;       string that is printed by that program, this program will bomb when
  1114.  ;       it spawns PRG_5AP.TOS, if a local stack is not declared here.
  1115.   
  1116.  lea        stack, a7           ; Point A7 to this program's stack.
  1117.  
  1118.  ;                    COMMAND LINE PROCESSING NOTE
  1119.  
  1120.  ;     At this point register A3 contains the address of the command line.
  1121.  ; In the algorithm below, the address of the first ASCII character in the
  1122.  ; command line input is stored at the pointer "program_name".  Then a NULL
  1123.  ; character is written over the carriage return code at the end of the
  1124.  ; command line input.  Thus the command line input itself becomes the
  1125.  ; string, the address of which must be pushed on the stack during the p_exec
  1126.  ; invocation.
  1127.  
  1128.  ;     Even though register A3 contains the address of the program name string,
  1129.  ; and the contents of A3 can be pushed during the p_exec invocation, the
  1130.  ; address of the string must be stored in a declared location because 
  1131.  ; register A3 might be used by the spawned program.  And the address of the
  1132.  ; string is still needed to print the spawned program's name in SPEED_3's
  1133.  ; output heading.
  1134.  
  1135. process_command_line:
  1136.  lea        command_line, a4    ; Fetch location to contain command line.
  1137.  movem.l    (a3), d0-d3         ; Move 16 bytes of command line to 4 registers.
  1138.  movem.l    d0-d3, (a4)         ; Move them to address "command_line".
  1139.  move.b     (a3)+, d0           ; Fetch parameter line input character count.
  1140.  ext.w      d0                  ; Extend to word for next instruction.
  1141.  move.b     #0, 1(a4,d0.w)      ; Store a null at end of string.
  1142.  lea        program_name, a0    ; Fetch address of pointer to program name.
  1143.  move.l     a3, (a0)            ; Store address of program name in pointer.
  1144.  move.b     #0, 0(a3,d0.w)      ; Replace $0D at end of program name with NULL.
  1145.          
  1146. insert_filename_suffix:
  1147.  move.b     #$44, -2(a4,d0.w)   ; Insert letter 'D'.
  1148.  move.b     #$41, -1(a4,d0.w)   ; Insert letter 'A'.
  1149.  move.b     #$54,  0(a4,d0.w)   ; Insert letter 'T'.
  1150.                       
  1151. create_file:
  1152.  move.w     #0, -(sp)           ; File attribute = read/write.
  1153.  pea        filename            ; Will be name of spawned process + .DAT.
  1154.  move.w     #$3C, -(sp)         ; Function = f_create = GEMDOS $3C.
  1155.  trap       #1                  ; File handle is returned in D0.
  1156.  addq.l     #8, sp
  1157.  lea        file_handle, a0
  1158.  move.w     d0, (a0)
  1159.  
  1160. redirect_output:                ; Exchange file handle with screen's handle.
  1161.  move.w     file_handle, -(sp)  ; This is the disk file's handle.
  1162.  move.w     #1, -(sp)           ; This is the video screen's handle.
  1163.  move.w     #$46, -(sp)         ; Function = f_force = GEMDOS $46.
  1164.  trap       #1
  1165.  addq.l     #6, sp
  1166.  
  1167. prepare_stack_for_load_and_execute_program:      
  1168.  pea        environ_string
  1169.  pea        command_string
  1170.  pea        (a3)                ; Push address of program name string.
  1171.  move.w     #0, -(sp)
  1172.  move.w     #$4B, -(sp)         ; Function = GEMDOS $4B = p_exec.
  1173.  
  1174. get_start_time:
  1175.  lea        start_time, a3      ; Fetch address of variable "start_time".
  1176.  trap       #3                  ; Returns value of system clock in D0.
  1177.  move.w     d0, (a3)            ; Save start time.
  1178. load_and_execute_program:
  1179.  trap       #1                 
  1180.  move.w     d0, d3              ; Copy after-load value to D3 for calculation.
  1181.  
  1182. get_end_time:
  1183.  trap       #3                  ; Returns value of system clock in D0.
  1184.  move.w     d0, d5              ; Copy to D5 for calculation.
  1185.  sub.w      d3, d5              ; Subtract after-load time from end time.
  1186.  ext.l      d5                  ; Extend to 32 bits.
  1187.  
  1188. reposition_stack_pointer:
  1189.  lea        $10(sp), sp        
  1190.  
  1191. get_drive:
  1192.  move.w     #$19, -(sp)         ; Function = dgetdrv = GEMDOS $19.
  1193.  trap       #1
  1194.  addq.l     #2, sp
  1195.  add.b      #'A', d0
  1196.  lea        drive, a0
  1197.  move.b     d0, (a0)
  1198.  
  1199. print_heading:
  1200.  lea        heading, a0
  1201.  bsr        print_string
  1202.  lea        program_name, a0     ; Fetch address of program name string.
  1203.  movea.l    (a0), a0
  1204.  bsr        print_string         ; Print spawned program's name.
  1205. print_drive_for_spawned_program:
  1206.  lea        drive_msg, a0        ; Print drive from which spawned program was
  1207.  bsr        print_string         ; loaded.
  1208.  
  1209. compute_load_time:
  1210.  lea        load_time_msg, a0
  1211.  bsr        print_string
  1212.  lea        start_time, a3
  1213.  sub.w      (a3), d3            ; Subtract start time from after-load time.
  1214.  ext.l      d3                  ; Extent to 32 bits.
  1215.  trap       #9                  ; See description in TRAPS.S.
  1216.  
  1217. close_file:
  1218.  move.w     file_handle, -(sp)
  1219.  move.w     #$3E, -(sp)         ; Function = fclose = GEMDOS $3E.
  1220.  trap       #1
  1221.  addq.l     #4, sp
  1222.  
  1223. terminate:
  1224.  move.w     #0, -(sp)
  1225.  trap       #1
  1226.  
  1227. print_string:                   ; Expects address of string to be in A0.
  1228.  move.l     a0, -(sp)           ; Push address of string onto stack.
  1229.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  1230.  trap       #1                  ; GEMDOS call
  1231.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  1232.  rts
  1233.  
  1234.  data
  1235. heading:          dc.b $D,$A,"SPEED_3.TTP Execution Results",$D,$A
  1236.                   dc.b       "for ",0
  1237. drive_msg:        dc.b ", loaded from drive: "
  1238. drive:            dc.b "A",$D,$A,0
  1239. load_time_msg:    dc.b $D,$A,"  Load time:    ",0
  1240. environ_string:   dc.b "TERM",0
  1241. command_string:   dc.b 0
  1242.  align
  1243.  bss
  1244. start_time:       ds.w    1
  1245. program_name:     ds.l    1     ; Pointer to string in basepage command line.
  1246. file_handle:      ds.w    1
  1247. command_line:     ds.b    1     ; Unused character count will go here.
  1248. filename:         ds.b   15     ; File name for redirected output.
  1249.                   ds.l   96     ; Program stack.
  1250. stack:            ds.l    0     ; Address of program stack.
  1251. program_end:      ds.l    0
  1252.  end
  1253.  
  1254.  
  1255. SPEED_3.TTP Execution Results
  1256.  
  1257. PRG_5AP.TOS Execution Results
  1258.  
  1259.   When executed from the desktop, this program will print this string on the
  1260.   video screen and pause for a keypress.  But, when this program is spawned by
  1261.   SPEED_1, SPEED_2, SPEED_3 or SPEEDTST, the string will be stored in a file 
  1262.   named PRG_5AP.DAT and the program will not pause for a  keypress.
  1263.  
  1264. SPEED_3.TTP Execution Results
  1265. for PRG_5AP.TOS, loaded from drive: G
  1266.  
  1267.   Load time:       40  milliseconds
  1268.   Execute time:   685  milliseconds
  1269.  
  1270.  
  1271.      Each of the three programs, SPEED_1, SPEED_2 and 
  1272. SPEED_3 are models which fixate attention to a particular 
  1273. phase of a continuous development cycle.  It would be very 
  1274. difficult, if not impossible, to pause as each instruction 
  1275. of each algorithm is chosen in order to describe the 
  1276. creative processes which instigate the choice.  Furthermore, 
  1277. the algorithmic development process is rhythmically 
  1278. recursive.  At intervals, the duration of which is dictated 
  1279. by personal education and experience, the programmer is 
  1280. drawn back to the beginning of the process to verify what 
  1281. has been done and, perhaps, to refine portions of the 
  1282. product.
  1283.      The final stage of the development process involves an 
  1284. assimilation of the best features of the three programs into 
  1285. a utility that is a fast as possible, while consuming 
  1286. minimum requisite memory.  In order to choose the best 
  1287. command line processing algorithm from the three that were 
  1288. introduced in the models, a comparison of their relative 
  1289. speeds and requisite memory is needed.  Program 20 was 
  1290. written to perform that chore.
  1291.   
  1292. Program 20. This program was used to compare the speeds of 
  1293. the command line processing algorithms used in programs 16, 
  1294. 18 and 19.
  1295.  
  1296.  ; Program Name: CMD_TEST.S
  1297.  ; Version 1.004
  1298.  
  1299.  ; Assembly Instructions:
  1300.  
  1301.  ;     Assemble in "PC-relative" mode and save with a TOS extension. 
  1302.  
  1303.  ; Execution Instructions:
  1304.  
  1305.  ;     Execute program CMD_TEST.TOS from the desktop.  After reading the
  1306.  ; program's output on the screen, terminate execution by pressing the
  1307.  ; Return key.
  1308.  
  1309.  ; Function:
  1310.  
  1311.  ;     This program is used to compare the relative speed of the command
  1312.  ; line processing algorithms used in SPEED_1, SPEED_2 and SPEED_3.
  1313.  
  1314.  ; Description:
  1315.  
  1316.  ;     Three command line processing algorithms are executed 10,000 times.
  1317.  ; The elapsed time and requisite memory for each algorithm is printed to
  1318.  ; the screen.  So that this program need not be executed as a TTP program,
  1319.  ; the command line is salted with a declared string.
  1320.  
  1321. release_excess_memory:
  1322.  lea        program_end, a0     ; Put "end of program" address in A0.
  1323.  movea.l    4(a7), a1           ; Put "basepage" address in A1.
  1324.  movea.l    a1, a5              ; Copy to A5 for command line access.
  1325.  trap       #6                  ; Calculate program size and release memory.
  1326.  lea        stack, a7           ; Point A7 to this program's stack.
  1327.  
  1328. mainline:
  1329.  lea        heading, a0
  1330.  bsr        print_string
  1331.  
  1332. salt_command_line:
  1333.  lea        salt, a0            ; Fetch pointer to ersatz command line input.
  1334.  movem.l    (a0), d0-d3         ; Move it to registers.
  1335.  movem.l    d0-d3, $80(a5)      ; Copy to actual command line address.
  1336.  
  1337. speed_1_algorithm:
  1338.  lea        speed_1_msg, a0
  1339.  bsr        print_string
  1340.  move.l     #9999, d4           ; Initialize counter for 10000 executions.
  1341.  trap       #3                  ; Get start time.
  1342.  move.l     d0, d5              ; Copy for calculations.
  1343. speed_1_loop:
  1344.  lea        $80(a5), a4         ; Fetch address of parameters.
  1345.  move.b     (a4)+, d0           ; Fetch parameter line character count.
  1346.  lea        program_name_1, a3  ; Load buffer address.
  1347.  subq.b     #1, d0              ; Set up counter.
  1348.  ext.w      d0                  ; Extend to match the size of the dbra
  1349.                                 ; instruction.
  1350. fetch_character:                
  1351.  move.b     (a4)+, (a3)+        ; Store character.
  1352.  dbra       d0, fetch_character ; Loop until D0 becomes negative.
  1353.  move.b     #0, (a3)            ; Finish with a NULL.
  1354. create_file_name:               ; Create a file to accept standard output.
  1355.  lea        filename_1, a4      ; Load buffer address.
  1356.  lea        program_name_1, a3  ; Load buffer address.
  1357. copy_name:
  1358.  move.b     (a3)+, (a4)+
  1359.  cmpi.b     #$2E, (a3)          ; Is next byte of program_name the period?
  1360.  bne.s      copy_name           ; Continue looping until period is seen.
  1361.  move.b     #$2E, (a4)+         ; Add a period.
  1362.  move.b     #$44, (a4)+         ; Add letter 'D'.
  1363.  move.b     #$41, (a4)+         ; Add letter 'A'.
  1364.  move.b     #$54, (a4)+         ; Add letter 'T'.
  1365.  move.b     #0, (a4)            ; Add a NULL.
  1366. speed_1_memory:
  1367.  dbra       d4, speed_1_loop    ; Loop until D4 becomes negative.
  1368.  trap       #3                  ; Get end time.
  1369.  bsr        convert_and_print_time
  1370.  
  1371. speed_2_algorithm:
  1372.  lea        speed_2_msg, a0
  1373.  bsr        print_string
  1374.  move.l     #9999, d4           ; Initialize counter for 10000 executions.
  1375.  trap       #3                  ; Get start time.
  1376.  move.l     d0, d5              ; Copy for calculations.
  1377. speed_2_loop:
  1378.  lea        input_line, a3      ; Fetch location to contain command line.
  1379.  lea        output_line_2, a4   ; A second location: for filename.
  1380.  movem.l    $80(a5), d0-d3      ; Move 16 bytes of command line to 4 registers.
  1381.  movem.l    d0-d3, (a3)         ; Move them to address "input_line".
  1382.  movem.l    d0-d3, (a4)         ; Move them to address "output_line".
  1383.  move.b     $80(a5), d0         ; Fetch command line ASCII character count.
  1384.  ext.w      d0                  ; Extend to word for next instruction.
  1385.  move.b     #0, 1(a3,d0.w)      ; Store a null at end of command line input.
  1386.  move.b     #0, 1(a4,d0.w)      ; Same for filename buffer.
  1387. insert_filename_suffix:
  1388.  move.b     #$44, -2(a4,d0.w)   ; Insert letter 'D'.
  1389.  move.b     #$41, -1(a4,d0.w)   ; Insert letter 'A'.
  1390.  move.b     #$54,  0(a4,d0.w)   ; Insert letter 'T'.
  1391. speed_2_memory:
  1392.  dbra       d4, speed_2_loop    ; Loop until D4 becomes negative.
  1393.  trap       #3                  ; Get end time.
  1394.  bsr        convert_and_print_time
  1395.  
  1396. speed_3_algorithm:
  1397.  lea        speed_3_msg, a0
  1398.  bsr        print_string
  1399.  move.l     #9999, d4           ; Initialize counter for 10000 executions.
  1400.  lea        $80(a5), a5         ; Fetch command line address.
  1401.  trap       #3                  ; Get start time.
  1402.  move.l     d0, d5              ; Copy for calculations.
  1403. speed_3_loop:
  1404.  
  1405.  ; NOTE: The first instruction, below, is not used in the actual SPEED_3
  1406.  ;       algorithm, but it must be included here to reset A3 to the
  1407.  ;       correct address each time through the loop.  This instruction
  1408.  ;       adds 4 clock periods per loop, 40000 clock periods for the
  1409.  ;       10000 loops, which is 5 milliseconds.  The accuracy of this error
  1410.  ;       calculation was confirmed by executing CMD_TEST.TOS with and 
  1411.  ;       without the instruction in the loop.  The 5 msec error is equal to
  1412.  ;       one system clock tick, therefore, when the loop end-time is obtained
  1413.  ;       with the trap #3 invocation, 1 clock tick is subtracted before the
  1414.  ;       loop time is calculated.
  1415.  
  1416.  ;       The memory occupied by this instruction is not included in the
  1417.  ;       value reported for the algorithm's requisite memory.
  1418.  
  1419.  movea.l    a5, a3
  1420. start_memory:
  1421.  lea        output_line_3, a4    ; Fetch location to contain command line.
  1422.  movem.l    (a3), d0-d3          ; Move 16 bytes of command line to 4 registers.
  1423.  movem.l    d0-d3, (a4)          ; Move them to address "command_line".
  1424.  move.b     (a3)+, d0            ; Fetch command line ASCII character count.
  1425.  ext.w      d0                   ; Extend to word for next instruction.
  1426.  move.b     #0, 1(a4,d0.w)       ; Store a null at end of string.
  1427.  lea        program_name_ptr, a0 ; Fetch address of pointer to program name.
  1428.  move.l     a3, (a0)             ; Store address of filename string in pointer.
  1429.  move.b     #0, 0(a3,d0.w)       ; Replace $0D at end of program name with NULL.
  1430.                        
  1431. _insert_filename_suffix:
  1432.  move.b     #$44, -2(a4,d0.w)   ; Insert letter 'D'.
  1433.  move.b     #$41, -1(a4,d0.w)   ; Insert letter 'A'.
  1434.  move.b     #$54,  0(a4,d0.w)   ; Insert letter 'T'.
  1435. speed_3_memory:
  1436.  dbra       d4, speed_3_loop
  1437.  trap       #3
  1438.  subq.w     #1, d0               ; Subtract 1 clock tick to correct time.
  1439.  bsr.s      convert_and_print_time
  1440.  
  1441. speed_1_requisite_memory:
  1442.  lea        speed_1_memory_msg, a0
  1443.  bsr.s      print_string
  1444.  lea        speed_1_loop, a1    ; Calculate number of bytes occupied by the
  1445.  lea        speed_1_memory, a0  ; instructions in the loop, then print.
  1446.  bsr.s      calculate_and_print_requisite_memory
  1447.  
  1448. speed_2_requisite_memory:
  1449.  lea        speed_2_memory_msg, a0
  1450.  bsr.s      print_string
  1451.  lea        speed_2_loop, a1     ; Calculate number of bytes occupied by the
  1452.  lea        speed_2_memory, a0   ; instructions in the loop, then store.
  1453.  bsr.s      calculate_and_print_requisite_memory
  1454.  
  1455. speed_3_requisite_memory:
  1456.  lea        speed_3_memory_msg, a0
  1457.  bsr        print_string
  1458.  lea        start_memory, a1    ; Calculate number of bytes occupied by the
  1459.  lea        speed_3_memory, a0  ; instructions in the loop, then print.
  1460.  bsr.s      calculate_and_print_requisite_memory
  1461.  
  1462. wait_for_keypress: 
  1463.  move.w     #8, -(sp)           ; Function = c_necin = GEMDOS $8.
  1464.  trap       #1                  ; GEMDOS call.
  1465.  addq.l     #2, sp              ; Reposition stack pointer at top of stack.
  1466.  
  1467. terminate:
  1468.  move.w     #0, -(sp)
  1469.  trap       #1
  1470.  
  1471. print_string:                   ; Expects address of string to be in A0.
  1472.  pea        (a0)                ; Push address of string onto stack.
  1473.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  1474.  trap       #1                  ; GEMDOS call
  1475.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  1476.  rts
  1477.  
  1478. convert_and_print_time:
  1479.  sub.l      d5, d0              ; Subtract start time from end time.
  1480.  mulu       #5, d0              ; Convert to milliseconds.
  1481.  move.l     d0, d1              ; Convert to ASCII decimal.
  1482.  trap       #4
  1483.  bsr        print_string
  1484.  lea        time_label, a0
  1485.  bsr        print_string
  1486.  rts
  1487.  
  1488. calculate_and_print_requisite_memory:
  1489.  suba.l     a1, a0
  1490.  move.l     a0, d1              ; Transfer requisite memory for trap call.
  1491. print_speed_1_requisite_memory:
  1492.  trap       #4                  ; Returns address of decimal string in A0.
  1493.  bsr        print_string
  1494.  lea        memory_label, a0
  1495.  bsr        print_string
  1496.  rts
  1497.  
  1498.  data
  1499. salt:               dc.b $B,"PRG_5AP.TOS",$D,0,0,0,0
  1500. heading:            dc.b $D,$A,"CMD_TEST Execution Results",$D,$A,$D,$A,0
  1501. speed_1_msg:        dc.b       "  SPEED_1 algorithm time: ",0
  1502. speed_2_msg:        dc.b       "  SPEED_2 algorithm time: ",0
  1503. speed_3_msg:        dc.b       "  SPEED_3 algorithm time: ",0
  1504. time_label:         dc.b " milliseconds",$D,$A,0
  1505. speed_1_memory_msg: dc.b $D,$A,"  SPEED_1 algorithm requisite memory: ",0
  1506. speed_2_memory_msg: dc.b       "  SPEED_2 algorithm requisite memory: ",0
  1507. speed_3_memory_msg: dc.b       "  SPEED_3 algorithm requisite memory: ",0
  1508. memory_label:       dc.b " bytes",$D,$A,0
  1509.  align
  1510.  bss
  1511. program_name_1:   ds.l    4   ; Program name buffer for SPEED_1 algorithm.
  1512. filename_1:       ds.l    4   ; Filename buffer for SPEED_1 algorithm.
  1513. input_line:       ds.b    1   ; Command line buffer for SPEED_2 algorithm.
  1514. program_name_2:   ds.b   15   ; Program name buffer for SPEED_2 algorithm.
  1515. output_line_2:    ds.b    1   ; Second command line buffer for SPEED_2.
  1516. filename_2:       ds.b   15   ; Filename buffer for SPEED_2 algorithm.
  1517. program_name_ptr: ds.l    4   ; Pointer to filename in command line for SPEED_3.
  1518. output_line_3:    ds.b    1   ; Command line buffer for SPEED_3 algorithm.
  1519. filename_3:       ds.b   15   ; Filename buffer for SPEED_3 algorithm.
  1520.                   ds.l   96   ; Program stack.
  1521. stack:            ds.l    0   ; Address of program stack.
  1522. program_end:      ds.l    0
  1523.  end
  1524.  
  1525.  
  1526. CMD_TEST Execution Results
  1527.  
  1528.   SPEED_1 algorithm time:  830 milliseconds
  1529.   SPEED_2 algorithm time:  350 milliseconds
  1530.   SPEED_3 algorithm time:  300 milliseconds
  1531.  
  1532.   SPEED_1 algorithm requisite memory:  60 bytes
  1533.   SPEED_2 algorithm requisite memory:  58 bytes
  1534.   SPEED_3 algorithm requisite memory:  52 bytes
  1535.  
  1536.  
  1537. Authenticating The Results
  1538.   
  1539.      Because the final configuration of the utility will 
  1540. depend primarily on the results displayed by CMD_TEST.TOS, 
  1541. the validity of those results must be beyond question.  I 
  1542. have used three validation techniques.  First, I single-
  1543. stepped through each instruction.  Then I verified the data 
  1544. written by the program to its basepage command line and bss 
  1545. section.  Finally, I compared the execution times reported 
  1546. to values calculated using the Motorola Programmer's 
  1547. Reference Manual.
  1548.      Figure 5.1 is a partial disassembly of program 20 as it 
  1549. was in memory after execution.  There you can see that the 
  1550. basepage command line contains the salt data and has been 
  1551. altered as specified by the SPEED_3 command line processing 
  1552. algorithm.  To wit: the carriage return has been replaced by 
  1553. a NULL.  Also evident are the strings stored in the bss 
  1554. segment by the three algorithms.  Table 5.1 lists the 
  1555. relevant declared variables, their lengths and their 
  1556. addresses in the disassembly listing.
  1557.  
  1558. Table 5.1 Match the variable names listed and their 
  1559. addresses to the data shown in the disassembly listing.
  1560.  
  1561.          Variable       Length    Address
  1562.  
  1563.      program_name_1:   ds.l    4  $0919C8
  1564.      filename_1:       ds.l    4  $0919D8
  1565.      input_line:       ds.b    1  $0919E8
  1566.      program_name_2:   ds.b   15  $0919E9
  1567.      output_line_2:    ds.b    1  $0919F8
  1568.      filename_2:       ds.b   15  $0919F9
  1569.      program_name_ptr: ds.l    4  $091A08
  1570.      output_line_3:    ds.b    1  $091A0C
  1571.      filename_3:       ds.b   15  $091A0D
  1572.   
  1573.  
  1574. Figure 5.1. Partial disassembly of CMD_TEST.TOS after 
  1575. execution, showing the basepage command line, and the 
  1576. command line relevant portion of the bss section.
  1577.  
  1578.  
  1579.  
  1580.  
  1581.  
  1582.  
  1583.  
  1584.  
  1585.  
  1586.  
  1587.  
  1588.  
  1589.  
  1590.  
  1591.  
  1592.  
  1593.  
  1594.  
  1595.  
  1596.  
  1597.  
  1598.  
  1599.  
  1600.  
  1601.  
  1602.      Table 5.2 lists the instructions used in each of the 
  1603. command line processing algorithms and their required 
  1604. execution clock periods as specified in the Motorola manual.  
  1605. I want to give you the values I calculated for two reasons; 
  1606. first, to show you that it can be done from the tables in 
  1607. the Motorola guide; second, to serve as verification that 
  1608. program 20 performs its task accurately.  If you desire, you 
  1609. can use this table to practice your interpretation of the 
  1610. data in the Motorola tables.  A short tutorial follows the 
  1611. table.
  1612.  
  1613. Table 5.2 The instructions used in command line processing 
  1614. algorithms of SPEED_1, SPEED_2 and SPEED_3.
  1615.  
  1616.       Instruction                       Clock Periods
  1617.  
  1618.       speed_1_loop:                    
  1619.        lea        $80(a5), a4                 8       
  1620.        move.b     (a4)+, d0                   8
  1621.        lea        program_name_1, a3          8
  1622.        subq.b     #1, d0                      4             
  1623.        ext.w      d0                          4
  1624.        Total = sum of 5 =                    32                  
  1625.                                
  1626.       fetch_character:
  1627.        (There are 11 characters.)               
  1628.        move.b     (a4)+, (a3)+               12       
  1629.        dbra       d0, fetch_character        10/14
  1630.        Total = 11(12) + 10(10) + 14 =       246
  1631.  
  1632.        move.b     #0, (a3)                   12
  1633.       create_file_name:              
  1634.        lea        filename_1, a4              8     
  1635.        lea        program_name_1, a3          8
  1636.        Total = sum of 3 =                    28
  1637.  
  1638.       copy_name:
  1639.        (There are 7 characters.)
  1640.        move.b     (a3)+, (a4)+               12
  1641.        cmpi.b     #$2E, (a3)                 12      
  1642.        bne.s      copy_name                  10/8
  1643.        Total = 7(24) + 6(10) + 8 =          236
  1644.          
  1645.        move.b     #$2E, (a4)+                12      
  1646.        move.b     #$44, (a4)+                12      
  1647.        move.b     #$41, (a4)+                12      
  1648.        move.b     #$54, (a4)+                12      
  1649.        move.b     #0, (a4)                   12
  1650.        Total = sum of 5 =                    60
  1651.  
  1652.        Algorithm total = 32 + 246 + 28 + 236 + 60 = 602
  1653.       speed_1_memory:
  1654.        dbra       d4, speed_1_loop
  1655.  
  1656.       speed_2_loop:
  1657.        lea        input_line, a3              8   
  1658.        lea        output_line_2, a4           8
  1659.        movem.l    $80(a5), d0-d3 = 16+8(4) = 48             
  1660.        movem.l    d0-d3, (a3)    =  8+8(4) = 40       
  1661.        movem.l    d0-d3, (a4)       8+8(4) = 40
  1662.        move.b     $80(a5), d0                12       
  1663.        ext.w      d0                          4                
  1664.        move.b     #0, 1(a3,d0.w)             18    
  1665.        move.b     #0, 1(a4,d0.w)             18    
  1666.       insert_filename_suffix:
  1667.        move.b     #$44, -2(a4,d0.w)          18
  1668.        move.b     #$41, -1(a4,d0.w)          18
  1669.        move.b     #$54,  0(a4,d0.w)          18
  1670.  
  1671.        Algorithm total = sum of 12 =        250
  1672.       speed_2_memory:
  1673.        dbra       d4, speed_2_loop
  1674.  
  1675.       speed_3_loop:
  1676.        movea.l    a5, a3 (not counted)
  1677.       start_memory:
  1678.        lea        output_line_3, a4           8  
  1679.        movem.l    (a3), d0-d3 = 12+8(4) =    44        
  1680.        movem.l    d0-d3, (a4)    8+8(4) =    40    
  1681.        move.b     (a3)+, d0                   8          
  1682.        ext.w      d0                          4                 
  1683.        move.b     #0, 1(a4,d0.w)             18     
  1684.        lea        program_name_ptr, a0        8 
  1685.        move.l     a3, (a0)                   12             
  1686.        move.b     #0, 0(a3,d0.w)             18       
  1687.                        
  1688.       _insert_filename_suffix:
  1689.        move.b     #$44, -2(a4,d0.w)          18 
  1690.        move.b     #$41, -1(a4,d0.w)          18 
  1691.        move.b     #$54,  0(a4,d0.w)          18
  1692.  
  1693.        Algorithm total = sum of 12 =        214
  1694.       speed_3_memory:
  1695.        dbra       d4, speed_3_loop
  1696.  
  1697.  
  1698. Instruction Execution Times Tutorial
  1699.   
  1700.      In my copy of the M68000 Programmer's Reference Manual, 
  1701. which may not be the same as yours, MC68000 instruction 
  1702. execution times are presented in Appendix D.  Times for the 
  1703. MC68008 are presented in Appendix E, and those for the 
  1704. MC68010/MC68012 are in Appendix F.  I am pointing out the 
  1705. locations for the other processors so that you can avoid 
  1706. them.  When you are looking for MC68000 times, make sure 
  1707. that you are doing so in Appendix D.
  1708.      The Introduction to the appendix contains information 
  1709. concerning wait states that is not applicable to the Atari 
  1710. ST.  The only thing in the introduction which concerns us 
  1711. are the notes stating that the instruction execution times 
  1712. are given in terms of external (system) clock periods and 
  1713. that the number of periods includes instruction fetch and 
  1714. all applicable operand fetches and stores.  The ST's clock 
  1715. period is 1 divided by 8,000,000 = .000000125 second = 1.25 
  1716. x 10-7 sec, because the system operates with an 8 megahertz 
  1717. (mhz) clock.
  1718.      The first table, D-1, lists the Effective Address 
  1719. Calculation Times for the addressing modes.  This table is 
  1720. one to which you must refer back when so directed by other 
  1721. tables.  Reference is made to this table via a + sign 
  1722. following the number of clock periods given for a particular 
  1723. instruction.  The reference indicates that you should go 
  1724. back to table D-1, fetch the appropriate time for the 
  1725. appropriate addressing mode and data length (byte,word or 
  1726. long) and add that time to the number of clock periods 
  1727. preceding the + sign.
  1728.      The other tables list base times for the instructions; 
  1729. I say base because of the need to add an effective address 
  1730. time for many instructions.  The tables are arranged so that 
  1731. data is presented for groups of similar instructions.  You 
  1732. use these tables by finding the one which lists the 
  1733. instruction of interest; then, if a source operand is 
  1734. involved, you locate the row specified by the source 
  1735. operand, if there are rows of source operands; then, if a 
  1736. destination operand is involved, you locate the column 
  1737. specified by the destination operand, if there are columns 
  1738. of destination operands; then, locate the data at the row-
  1739. column intersection; then, if a + sign follows the data, go 
  1740. back to table D-1, fetch the effective address time and add 
  1741. it to the data.
  1742.      When you have done all of that, you will have the 
  1743. instructions execution time in clock periods.  Not all 
  1744. instructions contain both a source operand and a destination 
  1745. operand.  Not all of the tables explicitly reference both 
  1746. operand types.  Not all tables list destination operands in 
  1747. columns; some of them list source operands in columns.  
  1748. Therefore, your mind must be on what you are doing when you 
  1749. are reading the tables.  For example, times for ADD/ADDA, 
  1750. AND, CMP/CMPA, DIVS, DIVU, EOR, MULS, MULU, OR and SUB are 
  1751. listed in table D-4.  Following each time given is a + sign, 
  1752. therefore, an effective address time from table D-1 is 
  1753. needed for each item in the table.
  1754.      You might ask, "Does the data in table D-4 pertain to 
  1755. source operands or destination operands?  Does the reference 
  1756. to table D-1 pertain to source operands or destination 
  1757. operands?".  The answer to both questions is, "Yes.".  
  1758. Because these instructions can be written so that the 
  1759. effective addresses which head the columned data in table D-
  1760. 4 can be either source or destination operands.  To see what 
  1761. I mean, look at the Assembler Syntax for the ADD 
  1762. instruction.  There you see the following notation:
  1763.  
  1764.                       ADD <ea>,Dn 
  1765.                       
  1766.                       ADD Dn,<ea>.
  1767.   
  1768.      The reason that Motorola's use of the term effective 
  1769. address is confusing is that, in their manual, all 
  1770. addressing modes are discussed as if the location specified 
  1771. in operands are somehow implied by operand format.  It seems 
  1772. as though the authors of the manual had originally intended 
  1773. that the term effective address be used to indicate a 
  1774. location specified by an operand to be ultimately found in 
  1775. memory external to the processor, in contrast to processor 
  1776. registers, which are internal addresses.
  1777.      But, in fact, even when discussing Register Direct 
  1778. Modes, the manual states, "These effective addressing modes 
  1779. specify that the operand is in one of the 16 multifunction 
  1780. registers.".  So, I say, let it all be effective addresses, 
  1781. as the authors apparently decided to do.  But then the 
  1782. descriptive effective is redundant, and it renders the 
  1783. instruction, add effective address calculation time, which 
  1784. is indicated by the + sign, ineffective.
  1785.      What that instruction should instruct one to do is 
  1786. this: for the appropriate operand, add the additional time 
  1787. indicated in table D-1 for the appropriate addressing mode.  
  1788. Of course, one must determine the appropriate operand and 
  1789. the appropriate addressing mode.  But this must be done 
  1790. regardless of terminology.  However, the manual does not 
  1791. make that clear, nor does it indicate the manner in which it 
  1792. can be accomplished.  I shall.
  1793.      Using instructions selected from those listed in table 
  1794. 5.2, I will conclude this tutorial by showing you how I 
  1795. obtained the execution time for those instruction, then I 
  1796. will show you how to obtain the time for at least one 
  1797. instruction listed in the Motorola tables which none of the 
  1798. instructions in table 5.2 access.  I think that the 
  1799. exploration will be sufficiently comprehensive.
  1800.  
  1801. lea input_line, a3
  1802.   
  1803.      The lea instruction is found in table D-10, JMP, JSR, 
  1804. LEA, PEA, and MOVEM Instruction Execution Times.  For all of 
  1805. these instructions, the destination operand is implicit: for 
  1806. JMP and JSR the destination is the program counter (PC); for 
  1807. LEA the destination is an address register; for PEA the 
  1808. destination is a stack; for MOVEM (M->R, memory to 
  1809. registers) the destination is a register group; for MOVEM 
  1810. (R->M, registers to memory) the destination is a group of 
  1811. memory addresses.  This means that the columns containing 
  1812. times in the table refer to source operands.
  1813.      The source operand for lea input_line, a3 is a label, 
  1814. therefore, the addressing mode used might seem to be 
  1815. absolute, but the program in which the instruction is used 
  1816. was assembled in AssemPro's PC-relative mode.  Therefore, 
  1817. the addressing mode is program counter with displacement.  
  1818. The execution time for the instruction is found where the 
  1819. LEA row intersects with the d16(PC) column.  The time is 8 
  1820. clock periods.
  1821.      Eight clock periods translates to 8(.000000125 sec) = 
  1822. .000001 sec = 1 microsecond = .001 msec.  As you can see, 
  1823. this is a very short period of time.  It is not possible to 
  1824. measure times that are this short with a clock that has a 
  1825. resolution of 5 msec.  That's why it is necessary to execute 
  1826. instructions and entire algorithms within loops that extend 
  1827. the time period being measured.  A time period being 
  1828. measured with the system clock should be sufficiently long 
  1829. to render the 5 msec resolution of the clock insignificant.
  1830.      Because the loops which execute the algorithms many 
  1831. times contain branching overhead, it is easier to compare 
  1832. relative execution times, instead of absolute execution 
  1833. times, when performing the comparisons with computer 
  1834. generated data.  When absolute times are desired, it is 
  1835. easier to compute them using the tables in the Motorola 
  1836. manual.
  1837.  
  1838. lea $80(a5), a4
  1839.   
  1840.      Here the addressing mode of the source operand is 
  1841. address register indirect with displacement.  The execution 
  1842. time for the instruction is found at the point of 
  1843. intersection specified by the LEA row and the d16(An) 
  1844. column.  The time is 8 clock periods.
  1845.  
  1846. movem.l $80(a5), d0-d3
  1847.   
  1848.      The row labeled MOVEM M->R is specified for this 
  1849. instruction.  Furthermore, this row is divided into two 
  1850. subrows: Word and Long.  The instruction specifies a 
  1851. longword operation, so the Long subrow must be used.  The 
  1852. source operand uses address register indirect with 
  1853. displacement addressing = d16(An).
  1854.      For this instruction, the data found at the 
  1855. intersection of the specified row and column is not the 
  1856. instruction execution time.  Instead, there is a formula 
  1857. from which the execution time must be calculated.  The 
  1858. parameter n specified by the formula is a variable for the 
  1859. number of registers specified in the instruction.  In this 
  1860. case the transfer from memory is to use 4 registers.  The 
  1861. instruction execution time is 16 + 8(4) = 48 clock periods.
  1862.  
  1863. movem.l d0-d3, (a3)
  1864.   
  1865.      Refer to the row labeled MOVEM R->M in the D-10 table.  
  1866. The formula shown at the intersection of the Long subrow and 
  1867. (An) column is similar to that for the MOVEM M->R 
  1868. instruction.  The instruction execution time is 8 + 8(4) = 
  1869. 40 clock periods.
  1870.  
  1871. move.b (a4)+, d0
  1872.   
  1873.      The execution times for move instructions are contained 
  1874. in two tables.  The first table, D-2 (Move Byte and Word 
  1875. Instruction Execution Times), must be used for this 
  1876. instruction because a byte operation is specified.  The 
  1877. addressing mode used by the source operand is address 
  1878. register indirect with postincrement.  That used in the 
  1879. destination operand is data register direct.  The 
  1880. instruction time of 8 clock periods is found at the 
  1881. intersection of the (An)+ row and the Dn column.  Note that 
  1882. these tables are used for MOVE and MOVEA instructions.
  1883.  
  1884. subq.b #1, d0
  1885.   
  1886.      The table to use is D-5 (Immediate Instruction 
  1887. Execution Times).  All of the instructions in this table 
  1888. require a source operand which uses the immediate data 
  1889. addressing mode.  The three columns in the table specify 
  1890. permissible destination operands.  In this case, the 
  1891. instruction specifies data register direct.  At the 
  1892. intersection of the SUBQ row and op #, Dn column, for a byte 
  1893. size operation, the time given is 4 clock periods.
  1894.  
  1895. ext.w d0
  1896.   
  1897.      This instruction found in table D-12 (Miscellaneous 
  1898. Instruction Execution Times).  Although there are two 
  1899. subrows shown for the EXT row, the times for both are 
  1900. identical.  This instruction requires no source operand, and 
  1901. the time is simply 4 clock periods.
  1902.  
  1903. dbra d0, fetch_character
  1904.   
  1905.      The DBcc instruction is used to control loop exits.  
  1906. Therefore, we are most often concerned with multiple 
  1907. executions of the instruction and with a sum of execution 
  1908. times.  Also, the execution time of a single DBcc execution 
  1909. depends on the state of the condition code register (CCR) 
  1910. and the state of the loop counter when loop exit takes 
  1911. place.  Loop exit is forced when the DBcc condition code 
  1912. becomes true or when the value in the counter becomes 
  1913. negative.
  1914.      Refer to table D-9 (Conditional Instruction Execution 
  1915. Times).  Note that the DBcc instruction is the only 
  1916. instruction in the table for which the displacement between 
  1917. the instruction and the destination does not affect the 
  1918. execution time.  Depending on the manual you are using, the 
  1919. DBcc row may be divided into 2 or 3 subrows.  Figure 5.4 
  1920. shows the row divided into 3 subrows.
  1921.  
  1922. Figure 5.4. Subrows for the DBcc Instruction.
  1923.  
  1924. Displacement      Branch Taken     Branch Not Taken
  1925.  
  1926.     cc true            -                 12
  1927.  
  1928. cc false, Count  
  1929.   Not Expired         10                  -
  1930.   
  1931. cc false, Counter
  1932.      Expired           -                 14
  1933.  
  1934.  
  1935.      The information contained in the second and third rows 
  1936. can be combined so that only one row need be used to express 
  1937. it.  In that case, the second row would be:
  1938.  
  1939.     cc false          10                 14
  1940.   
  1941. This makes sense because when cc is false the branch can be 
  1942. taken only if the count has not expired, while it cannot be 
  1943. taken if the count has expired.
  1944.      Except for the DBT instruction, which never branches 
  1945. and never decrements, for any condition specified in a DBcc 
  1946. instruction (For DBRA = DBF, the condition is always 
  1947. false.), a branch will be taken if the condition is true or 
  1948. if the value in the counter is not negative, and the 
  1949. execution time for the instruction will be 10 clock periods.  
  1950. If the condition becomes true, a branch will not be taken, 
  1951. and the execution time for the instruction will be 12 clock 
  1952. periods, regardless of the value in the counter.  If the 
  1953. value in the counter becomes negative before the condition 
  1954. becomes true, then the execution time for the instruction 
  1955. will be 14 clock periods.
  1956.      For a counter value n, the DBcc instruction will be 
  1957. executed N times if exit from the loop takes place because 
  1958. the condition becomes true and the sum of DBcc  instruction 
  1959. execution times will be (N)(10) + 12, where N is the number 
  1960. of branches which actually took place, not the value stored 
  1961. in the counter.   The sum of  execution times will be 
  1962. (n)(10) + 14 if exit from the loop takes place because the 
  1963. counter becomes negative.
  1964.      For the instruction being used as an example, n is 
  1965. equal to one less than the number of characters in the 
  1966. string being copied.  There are 11 characters, so n equals 
  1967. 10 because the value in the counter must be one less than 
  1968. the number of times the loop is to be executed.  The 
  1969. condition for the DBRA instruction is never true, so exit 
  1970. from the loop can only take place when the value in the 
  1971. counter becomes negative.  The sum of execution times for 
  1972. the instruction is (10)(10) + 14 = 114 clock periods.
  1973.  
  1974. cmpi.b #$2E, (a3)
  1975.   
  1976.      This instruction is found in table D-5 (Immediate 
  1977. Instruction Execution Times).  This table was discussed in 
  1978. the section under subq.b #1, d0.  The source operand must 
  1979. use, and does use, the immediate data addressing mode.  
  1980. Unlike that of the previously referenced instruction, the 
  1981. destination operand of this one uses the address register 
  1982. indirect addressing mode.  And at the intersection of the 
  1983. CMPI.B row and op #, M column, we find that the instruction 
  1984. execution time of 8 clock periods is following by a + sign.
  1985.     The + sign indicates a reference to table D-1 (Effective 
  1986. Address Calculation Times).  But what value is it that we 
  1987. seek there?  Just under the heading for table D-5 is the 
  1988. statement that implies this information.  The statement 
  1989. tells us that the time shown at the intersection is that 
  1990. which is required to fetch the immediate operand.
  1991.      We can deduce that the time we seek is that for the 
  1992. addressing mode of the destination operand.  In table D-1, 
  1993. at the (An) row/byte size operation intersection we find the 
  1994. value 4, which means that we must add 4 clock periods to the 
  1995. 8 shown in table D-5.  Thus the instruction execution time 
  1996. is 12 clock periods.
  1997.  
  1998. bne.s copy_name
  1999.   
  2000.      The Bcc instruction is listed in table D-9, the same 
  2001. table which lists the DBcc instruction.  The Bcc instruction 
  2002. also has a Branch Taken and a Branch Not Taken column; and 
  2003. like the DBcc instruction, the Bcc instruction's execution 
  2004. time depends on the state of the CCR; but unlike the DBcc 
  2005. instruction, it also depends on the size of the displacement 
  2006. between the instruction and the branch destination.
  2007.      For the instruction being discussed, the displacement 
  2008. is short = byte size.  For a byte size displacement the 
  2009. execution time is 10 clock periods for a branch taken, 8 
  2010. clock periods for a branch not taken.  There are two 
  2011. instructions within the SPEED_1 copy_name loop, each of 
  2012. which require 12 clock periods per execution.  The body of 
  2013. the loop is executed 7 times, and the bne.s instruction is 
  2014. executed 7 times.  But the branch is taken only 6 times.  
  2015. The sum of the Bcc instruction execution times will be 6(10) 
  2016. + 8 = 68 clock periods.
  2017.  
  2018. add.l d0, d5
  2019.   
  2020.      Refer to table D-4, Standard Instruction Execution 
  2021. Times.  There are two subrows, labeled according to the size 
  2022. of the operation.  At the intersection of the Long subrow 
  2023. and the op<ea>, Dn column, there is this notation: 
  2024. 6(1/0)+**.  Referring to the notes under the table, we find 
  2025. that the + means that we must fetch the address calculation 
  2026. time for the source operand, and the ** means that the 6 
  2027. must be increased to 8 if the addressing mode of the source 
  2028. operand is register direct or immediate.
  2029.      Well, the addressing mode of the source operand is 
  2030. register direct, so the 6 becomes 8.  Glancing back at table 
  2031. D-1, we see that the address calculation time for the 
  2032. register direct addressing mode is 0.  Therefore, the 
  2033. execution time for the instruction is simply 8 clock 
  2034. periods.
  2035.  
  2036. asl #2, d5
  2037.   
  2038.      The execution times for SHIFT and ROTATE instructions 
  2039. are listed in table D-7.  Using the formula shown at the 
  2040. intersection of the ASL instruction's Long subrow and the 
  2041. Register column, the calculated execution time for the 
  2042. example is 8 + 2(2) = 12 clock periods.  Here I have 
  2043. replaced n with the immediate value of the source operand.
  2044.  
  2045. seq (a0)
  2046.   
  2047.      Refer to table D-6, Single Operand Instruction 
  2048. Execution Times.  The Scc instruction row is divided into 
  2049. two subrows labeled Byte, False and Byte, True.  So we see 
  2050. that the execution time depends on the state of the 
  2051. condition code, which is eq in the example, if the 
  2052. addressing mode of the operand is register direct.
  2053.      For all other modes, the execution time is 8 clock 
  2054. periods plus the address calculation time obtained from 
  2055. table D-1.  The example operand's addressing mode is address 
  2056. register indirect, and in table D-1 the address calculation 
  2057. time for that mode is 4 clock periods for a byte size 
  2058. operation.  The instruction execution time is 8 + 4 = 12 
  2059. clock periods.
  2060.  
  2061. bset #5, (sp)
  2062.   
  2063.      Table D-8 lists the execution times for the Bit 
  2064. Manipulation instructions.  For all of the instructions 
  2065. listed in the table, the bit to be manipulated is specified 
  2066. by the source operand; the location of the bit to be 
  2067. manipulated is specified by the destination operand.
  2068.      There are two major columns in this table: Dynamic and 
  2069. Static.  The Dynamic major column is used if the number of 
  2070. the bit to be manipulated is specified with the contents of 
  2071. a register; the Static major column is used if the number of 
  2072. the bit to be manipulated is specified with immediate data, 
  2073. such as shown in the example.
  2074.      Each of the major columns is composed of two minor 
  2075. columns.  A Register minor column is used if the bit to be 
  2076. manipulated resides in a register, a Memory minor column is 
  2077. used if the bit to be manipulated resides in memory external 
  2078. to the processor.  The bit to be manipulated in the example 
  2079. resides in a stack, which is memory external to the 
  2080. processor.
  2081.      An operation size indicator for any of the instructions 
  2082. shown in this table would be redundant because the size of 
  2083. the operation must be long if the bit to be manipulated 
  2084. resides in a register and it must be byte otherwise.  So at 
  2085. the intersection of BSET's Byte subrow and Static-Memory 
  2086. column we find the notation: 12(2/1)+.  Fetching the address 
  2087. calculation time for (sp) = (An) from table D-1, which is 4 
  2088. for a byte size operation, and adding it to the 12, we 
  2089. calculate the example instruction time as 16 clock periods.
  2090.      This concludes my Instruction Execution Times Tutorial.  
  2091. I have not dealt with table D-13, which lists the single 
  2092. instruction MOVEP, because this instruction is a little 
  2093. tricky.  I will use this instruction in a later chapter, and 
  2094. I hope to remember to discuss its execution time then.  
  2095. Neither have I dealt with table D-14, which lists Exception 
  2096. Processing Execution Times because they are so easily 
  2097. derived.  For example, the execution time for any trap #n 
  2098. instruction is simply 34 clock periods.
  2099.  
  2100. Execution Speed Ratios
  2101.  
  2102.      The execution speed ratios of figure 5.2 are obtained 
  2103. from the results of one execution of CMD_TEST.TOS.  On 
  2104. subsequent executions the results for SPEED_1 were sometimes 
  2105. 835, and the results for SPEED_3 were sometimes 305.  At 
  2106. times, both differences appeared simultaneously.  These 
  2107. differences for multiple executions are to be expected 
  2108. because the system variable _hz_200 (memory location $4BA) 
  2109. is incremented only every 200hz, which means that the period 
  2110. between increments is 1/200 = .005 second = 5 milliseconds.  
  2111. This means that the variable measures time with a resolution 
  2112. of 5 milliseconds (msec).
  2113.      Unexpectedly, the time for SPEED_2 rarely varied.  At 
  2114. first, that made me wonder if I had made an error in its 
  2115. algorithm as it is in CMD_TEST.TOS, but I have checked 
  2116. extensively and found nothing wrong.  However, I mention my 
  2117. concern, just so you'll know, although it does not really 
  2118. affect the decision concerning which algorithm to choose for 
  2119. SPEEDTST.TTP.
  2120.  
  2121.   
  2122. Figure 5.2. CMD_TEST.TOS execution speed ratios.  As you can 
  2123. see, SPEED_2's command line processing algorithm is about 
  2124. 2.37 times faster than SPEED_1's, while SPEED_3's is about 
  2125. 2.77 times faster.
  2126.  
  2127.                  SPEED_1   830
  2128.                  ------- = --- = 2.37
  2129.                  SPEED_2   350
  2130.  
  2131.                  SPEED_1   830
  2132.                  ------- = --- = 2.77
  2133.                  SPEED_3   300
  2134.  
  2135.                  SPEED_2   350
  2136.                  ------- = --- = 1.17
  2137.                  SPEED_3   300
  2138.  
  2139.  
  2140.       The execution speed ratios shown in figure 5.3 are 
  2141. obtained from the data in table 5.2.  I have also checked 
  2142. and rechecked this data many times, but I warn you not to 
  2143. trust me, although I trust the data.  Actually, the ratios 
  2144. below agree very closely with the those of figure 5.2, 
  2145. especially when one considers the 5 msec resolution of the 
  2146. clock that is being used to measure execution time.  In any 
  2147. case, we are much more interested in relative execution 
  2148. speeds than we are in absolute speeds.
  2149.   
  2150. Figure 5.3. Execution speed ratios calculated from 
  2151. instruction execution timing information in the Motorola 
  2152. manual.
  2153.  
  2154.  
  2155.                  SPEED_1   602
  2156.                  ------- = --- = 2.41
  2157.                  SPEED_2   250
  2158.  
  2159.                  SPEED_1   602
  2160.                  ------- = --- = 2.81
  2161.                  SPEED_3   214
  2162.  
  2163.                  SPEED_2   250
  2164.                  ------- = --- = 1.17
  2165.                  SPEED_3   214
  2166.  
  2167.  
  2168. Putting the Pieces Together
  2169.   
  2170.      The final algorithm is prepared by extracting the best 
  2171. algorithms from the three models, and installing the 
  2172. instructions implemented by custom trap #9.  All of the 
  2173. programs of the series, SPEED_1.TTP, SPEED_2.TTP and 
  2174. SPEED_3.TTP, as well as programs PRG_5AP.TOS, CMD_TEST.TOS, 
  2175. TRAPS.S and TRAP_9.S along with all of the execution results 
  2176. are included in the documentation package for program 21.  
  2177. In addition, program 21 contains some documentation that was 
  2178. not previously disclosed.
  2179.  
  2180. Program 21. The final algorithm.
  2181.  
  2182.  ; Program Name: SPEEDTST.S
  2183.  ; Version: 1.006
  2184.  
  2185.  ; Assembly Instructions:
  2186.  
  2187.  ;     Assemble in "PC-relative" mode and save with a TTP extension. 
  2188.  
  2189.  ; Function:
  2190.  
  2191.  ;     Spawn the TOS or PRG process typed on the command line.  Create a disk
  2192.  ; file which is to be identified by the name of the spawned program with a
  2193.  ; DAT suffix.  The disk file is to reside in the same directory as does the
  2194.  ; spawned process.
  2195.  
  2196.  ;     Calculate the spawned program's load and execution times and store them
  2197.  ; in the file.  If the spawned process directs output to the video screen via
  2198.  ; GEMDOS function $9, redirect that output to the file.
  2199.  
  2200.  ; Execution Instructions:
  2201.  
  2202.  ;     SPEEDTST.TTP will not execute unless the custom traps in program
  2203.  ; TRAPS.PRG have previously been installed.
  2204.  
  2205.  ;     Execute from the desktop.  Type the name of an executable file which
  2206.  ; has a TOS or PRG extension on SPEEDTST.TTP's input parameter line.  The
  2207.  ; name of the program you type on the parameter line must be in the same
  2208.  ; directory as is SPEEDTST.TTP.  The program must terminate with GEMDOS
  2209.  ; function $4C, and, via that function, it must pass to SPEEDTST.TTP the
  2210.  ; word length portion of the value that was in memory location $4BA
  2211.  ; immediately after it was loaded.
  2212.  
  2213.  ;     The longword value in $4BA can be obtained by invoking custom trap #3
  2214.  ; (get_time).  SPEEDTST.TTP uses the word length portion of that value,
  2215.  ; which is returned in D0 by GEMDOS $4C, to calculate the spawned program's
  2216.  ; load and execution times.
  2217.  
  2218.  ;     If the spawned program contains any instructions that cause it to pause,
  2219.  ; such as those that wait for a keypress or some other event, those should be
  2220.  ; commented out, and the program should be assembled especially for the speed
  2221.  ; test.  Otherwise the execution time computed by SPEEDTEST.TTP will include
  2222.  ; the time that the spawned program was waiting for the event to occur.  
  2223.  
  2224.  ;     If custom trap #8 is used to terminate the spawned program, the trap
  2225.  ; will execute a wait_for_keypress algorithm when the program is executed from
  2226.  ; the desktop, but it will omit the wait algorithm when the program is spawned
  2227.  ; by SPEEDTST.TTP.  In addition, trap #8 will return the after-load value to
  2228.  ; SPEEDTST.TTP and terminate the spawned program with GEMDOS function $4C.
  2229.  
  2230.  ;     Both trap #8 and SPEEDTST.TTP require that the spawned program be
  2231.  ; initialized with custom trap #6 or a similar algorithm.  See TRAPS.S for
  2232.  ; details about custom traps #6 and #8.
  2233.  
  2234. release_excess_memory:
  2235.  lea        -$82(pc), a3        ; Put "command line" address in A3.
  2236.  lea        -$80(a3), a1        ; Put "basepage" address in A1.
  2237.  lea        program_end, a0     ; Put "end of program" address in A0.
  2238.  trap       #6                  ; Calculate program size and release memory.
  2239.  
  2240.  ; NOTE: A local stack is not declared in PRG_5AP.TOS.  Because of the long
  2241.  ;       string that is printed by that program, this program will bomb when
  2242.  ;       it spawns PRG_5AP.TOS, if a local stack is not declared here.
  2243.  
  2244.  lea        stack, a7           ; Point A7 to this program's stack.
  2245.  
  2246. process_command_line:
  2247.  lea        command_line, a4    ; Fetch location to contain command line.
  2248.  movem.l    (a3), d0-d3         ; Move 16 bytes of command line to 4 registers.
  2249.  movem.l    d0-d3, (a4)         ; Move them to address "command_line".
  2250.  move.b     (a3)+, d0           ; Fetch command line ASCII character count.
  2251.  ext.w      d0                  ; Extend to word for next instruction.
  2252.  move.b     #0, 1(a4,d0.w)      ; Store a null at end of string.
  2253.  
  2254.  lea        program_name, a0    ; Fetch address of pointer to command line.
  2255.  move.l     a3, (a0)            ; Store address of command line string at
  2256.                                 ; pointer.
  2257.  move.b     #0, 0(a3,d0.w)      ; Replace $0D at end of command line input
  2258.                                 ; in basepage with a NULL.
  2259.  
  2260. insert_filename_suffix:
  2261.  move.b     #$44, -2(a4,d0.w)   ; Insert letter 'D'.
  2262.  move.b     #$41, -1(a4,d0.w)   ; Insert letter 'A'.
  2263.  move.b     #$54,  0(a4,d0.w)   ; Insert letter 'T'.
  2264.  
  2265. create_file:
  2266.  move.w     #0, -(sp)           ; File attribute = read/write.
  2267.  pea        filename            ; Will be name of spawned process + .DAT.
  2268.  move.w     #$3C, -(sp)         ; Function = f_create = GEMDOS $3C.
  2269.  trap       #1                  ; File handle is returned in D0.
  2270.  addq.l     #8, sp
  2271.  lea        file_handle, a0     ; Store returned file handle.
  2272.  move.w     d0, (a0)
  2273.  
  2274. redirect_output:                ; Exchange file handle with screen's handle.
  2275.  move.w     file_handle, -(sp)  ; This is the disk file's handle.
  2276.  move.w     #1, -(sp)           ; This is the video screen's handle.
  2277.  move.w     #$46, -(sp)         ; Function = f_force = GEMDOS $46.
  2278.  trap       #1
  2279.  addq.l     #6, sp
  2280.  
  2281. prepare_stack_for_load_and_execute_program:      
  2282.  pea        environ_string
  2283.  pea        command_string
  2284.  pea        (a3)                ; Push address of program name string.
  2285.  move.w     #0, -(sp)
  2286.  move.w     #$4B, -(sp)         ; Function = GEMDOS $4B = p_exec.
  2287. get_start_time:
  2288.  lea        start_time, a3      ; Fetch address of variable "start_time".
  2289.  trap       #3                  ; Returns value of system clock in D0.
  2290.  move.w     d0, (a3)            ; Save start time.
  2291. load_and_execute_program:
  2292.  trap       #1                 
  2293.  move.w     d0, d3              ; Copy after-load value to D3 for calculation.
  2294.  
  2295. get_end_time:
  2296.  trap       #3                  ; Returns value of system clock in D0.
  2297.  move.w     d0, d5              ; Copy to D5 for calculation.
  2298.  sub.w      d3, d5              ; Subtract after-load time from end time.
  2299.  ext.l      d5                  ; Extend to 32 bits.
  2300.  
  2301. reposition_stack_pointer:
  2302.  lea        $10(sp), sp        
  2303.  
  2304. get_drive:
  2305.  move.w     #$19, -(sp)         ; Function = dgetdrv = GEMDOS $19.
  2306.  trap       #1                  ; Returns 0 for drive A, 1 for B, etc.
  2307.  addq.l     #2, sp              
  2308.  add.b      #$41, d0            ; Add ASCII value for A to compute ASCII
  2309.  lea        drive, a0           ; letter code for the drive value returned.
  2310.  move.b     d0, (a0)            ; Save drives ASCII leter code.
  2311.  
  2312. print_heading:
  2313.  lea        heading, a0
  2314.  bsr        print_string
  2315.  lea        program_name, a0     ; Fetch address of program name string.
  2316.  movea.l    (a0), a0
  2317.  bsr        print_string
  2318. print_drive_for_spawned_program:
  2319.  lea        drive_msg, a0
  2320.  bsr        print_string
  2321.  
  2322. compute_load_time:
  2323.  lea        load_time_msg, a0
  2324.  bsr        print_string
  2325.  lea        start_time, a3
  2326.  sub.w      (a3), d3            ; Subtract start time from after-load time.
  2327.  ext.l      d3                  ; Extent to 32 bits.
  2328.  
  2329. multiply_by_five:               ; Convert to milliseconds.
  2330.  move.l     d3, d0              ; Save a copy to add.
  2331.  asl.l      #2, d3              ; Shift to multiply by 4.
  2332.  add.l      d0, d3              ; To complete multiplication by 5.
  2333.  
  2334. print_load_time:
  2335.  cmpi.l     #999, d3            ; If load time is less than 1000, then
  2336.  bgt        no_space            ; print a leading blank space for output
  2337.  lea        space, a0           ; alignment.
  2338.  bsr        print_string
  2339.  cmpi.l     #99, d3             ; If load time is less than 100, then
  2340.  bgt        no_space            ; print another leading blank space.
  2341.  lea        space, a0
  2342.  bsr        print_string
  2343. no_space:
  2344.  move.l     d3, d1              ; Copy load time to D1 for decimal conversion.       
  2345.  trap       #4                  ; Returns address of decimal string in A0.
  2346.  bsr.s      print_string
  2347.  lea        units_label, a0
  2348.  bsr.s      print_string
  2349.  
  2350. compute_execution_time:         ; D5 already contains the execution time.
  2351.  lea        execute_time_msg, a0; Here, it must only be multiplied by 5 to
  2352.  bsr.s      print_string        ; be converted to milliseconds.
  2353.  move.l     d5, d0              ; Save a copy to add.
  2354.  asl.l      #2, d5              ; Shift to multiply by 4.
  2355.  add.l      d0, d5              ; To complete multiplication by 5.
  2356.  
  2357. print_execution_time:
  2358.  cmpi.l     #999, d5            ; If execute time is less than 1000, then
  2359.  bgt        _no_space           ; print a leading blank space for output
  2360.  lea        space, a0           ; alignment.
  2361.  bsr        print_string
  2362.  cmpi.l     #99, d5             ; If execute time is less than 100, then
  2363.  bgt        _no_space           ; print another leading blank space.
  2364.  lea        space, a0
  2365.  bsr        print_string
  2366. _no_space:
  2367.  move.l     d5, d1              ; Copy execute time for decimal conversion.
  2368.  trap       #4                  ; Returns address of decimal string in A0.
  2369.  bsr.s      print_string
  2370.  lea        units_label, a0
  2371.  bsr.s      print_string
  2372.  
  2373. close_file:
  2374.  move.w     file_handle, -(sp)
  2375.  move.w     #$3E, -(sp)         ; Function = fclose = GEMDOS $3E.
  2376.  trap       #1
  2377.  addq.l     #4, sp
  2378.  
  2379. terminate:
  2380.  move.w     #0, -(sp)
  2381.  trap       #1
  2382.  
  2383. print_string:                   ; Expects address of string to be in A0.
  2384.  pea        (a0)                ; Push address of string onto stack.
  2385.  move.w     #9, -(sp)           ; Function = c_conws = GEMDOS $9.
  2386.  trap       #1                  ; GEMDOS call
  2387.  addq.l     #6, sp              ; Reset stack pointer to top of stack.
  2388.  rts
  2389.  
  2390.  data
  2391. space:            dc.b " ",0
  2392. heading:          dc.b $D,$A,"SPEEDTST.TTP Execution Results",$D,$A
  2393.                   dc.b       "for ",0
  2394. drive_msg:        dc.b ", loaded from drive: "
  2395. drive:            dc.b "A",$D,$A,0
  2396. load_time_msg:    dc.b $D,$A,"  Load time:      ",0
  2397. execute_time_msg: dc.b       "  Execution time: ",0
  2398. units_label:      dc.b " milliseconds",$D,$A,0
  2399. environ_string:   dc.b "TERM",0
  2400. command_string:   dc.b 0
  2401.  align
  2402.  bss
  2403. start_time:       ds.w    1     ; Value in $4BA just before spawning.
  2404. program_name:     ds.l    1     ; Pointer to name in basepage command line.
  2405. file_handle:      ds.w    1     ; Handle for the filename below.
  2406. command_line:     ds.b    1     ; Unused character count will go here.
  2407. filename:         ds.b   15     ; File name for redirected output.
  2408.                   ds.l   96     ; Program stack.
  2409. stack:            ds.l    0     ; Address of program stack.
  2410. program_end:      ds.l    0
  2411.  end
  2412.  
  2413.  
  2414. SPEEDTST.TTP Execution Results
  2415.  
  2416. PRG_5AP.TOS Execution Results
  2417.  
  2418.   When executed from the desktop, this program will print this string on the
  2419.   video screen and pause for a keypress.  But, when this program is spawned by
  2420.   SPEED_1, SPEED_2, SPEED_3 or SPEEDTST, the string will be stored in a file 
  2421.   named PRG_5AP.DAT and the program will not pause for a  keypress.
  2422.  
  2423. SPEEDTST.TTP Execution Results
  2424. for PRG_5AP.TOS, loaded from drive: G
  2425.  
  2426.   Load time:         40 milliseconds
  2427.   Execution time:   685 milliseconds
  2428.  
  2429.  
  2430. The Second Utility
  2431.   
  2432.      I conclude this chapter with a utility that spawns a 
  2433. program and create a file for redirected output, but which 
  2434. does not measure load and execution times.  This program is 
  2435. used when I want to save the output from a program in a disk 
  2436. file for documentation, leisurely viewing or for comparison 
  2437. with the output of one or more other programs.  Program 22 
  2438. is simply a subset of program 21.
  2439.  
  2440. Program 22. A program that simply spawns a process and saves 
  2441. its redirected output in a disk file.
  2442.  
  2443.  ; Program Name: SPAWN.S
  2444.  ; Version 1.003
  2445.  
  2446.  ; Assembly Instructions:
  2447.  
  2448.  ;     Assemble in "PC-relative" mode and save with a TTP extension. 
  2449.  
  2450.  ; Program Function:
  2451.  
  2452.  ;     Spawn the TOS or PRG process typed on the command line.  Create a disk
  2453.  ; file which is to be identified by the name of the spawned program with a
  2454.  ; DAT suffix.  The disk file is to reside in the same directory as does the
  2455.  ; spawned process.
  2456.  
  2457.  ;     If the program to be executed has any halt or wait instructions, such
  2458.  ; as wait for a keypress, etc., you must remember that execution of the
  2459.  ; spawned process will not terminate until those conditions are satisfied.
  2460.  
  2461. release_excess_memory:
  2462.  lea        -$82(pc), a3        ; Put "command line" address in A3.
  2463.  lea        -$80(a3), a1        ; Put "basepage" address in A1.
  2464.  lea        program_end, a0     ; Put "end of program" address in A0.
  2465.  trap       #6                  ; Calculate program size and release memory.
  2466.  lea        stack, a7           ; Point A7 to this program's stack.
  2467.  
  2468. process_command_line_parameters:
  2469.  lea        command_line, a4    ; Fetch location to contain command line.
  2470.  movem.l    (a3), d0-d3         ; Move 16 bytes of command line to 4 registers.
  2471.  movem.l    d0-d3, (a4)         ; Move them to address "command_line".
  2472.  move.b     (a3)+, d0           ; Fetch command line ASCII character count.
  2473.  ext.w      d0                  ; Extend to word for next instruction.
  2474.  move.b     #0, 1(a4,d0.w)      ; Store a null at end of string.
  2475.  
  2476.  lea        program_name, a0    ; Fetch address of pointer to command line.
  2477.  move.l     a3, (a0)            ; Store address of command line string at
  2478.                                 ; pointer.
  2479.  move.b     #0, 0(a3,d0.w)      ; Replace $0D at end of command line input
  2480.                                 ; in basepage with a NULL.
  2481. insert_filename_suffix:
  2482.  move.b     #$44, -2(a4,d0.w)   ; Insert letter 'D'.
  2483.  move.b     #$41, -1(a4,d0.w)   ; Insert letter 'A'.
  2484.  move.b     #$54,  0(a4,d0.w)   ; Insert letter 'T'.
  2485.  
  2486. create_file:              
  2487.  move.w     #0, -(sp)           ; File attribute = read/write.
  2488.  pea        filename            ; Will be name of spawned process + .DAT.
  2489.  move.w     #$3C, -(sp)         ; Function = f_create = GEMDOS $3C.
  2490.  trap       #1                  ; File handle is returned in D0.
  2491.  addq.l     #8, sp
  2492.  lea        file_handle, a0     ; Store returned file handle to be used when
  2493.  move.w     d0, (a0)            ; the file is closed later.
  2494.  
  2495. redirect_output:                ; Exchange file handle with screen's handle.
  2496.  move.w     d0, -(sp)           ; This is the disk file's handle.
  2497.  move.w     #1, -(sp)           ; This is the video screen's handle.
  2498.  move.w     #$46, -(sp)         ; Function = f_force = GEMDOS $46.
  2499.  trap       #1
  2500.  addq.l     #6, sp
  2501.  
  2502. load_and_execute_program:     
  2503.  pea        environ_string
  2504.  pea        command_string
  2505.  pea        (a3)                ; A3 contains address of program name string.
  2506.  move.w     #0, -(sp)           ; Load and Go option.
  2507.  move.w     #$4B, -(sp)         ; Function = GEMDOS $4B = p_exec.
  2508.  trap       #1                 
  2509.  lea        $10(a7), sp         ; Reposition stack pointer.         
  2510.  
  2511. close_file:                    
  2512.  move.w     file_handle, -(sp) 
  2513.  move.w     #$3E, -(sp)         ; Function = GEMDOS $3E = f_close.
  2514.  trap       #1
  2515.  addq.l     #4, sp
  2516.  
  2517. terminate:                
  2518.  move.w     #0, -(sp)
  2519.  trap       #1
  2520.  
  2521.  data
  2522. environ_string:  dc.b    "TERM",0
  2523. command_string:  dc.b    0
  2524.  align
  2525.  bss
  2526. file_handle:     ds.w    1     ; Handle for the disk file named below.
  2527. command_line:    ds.b    1     ; Unused character count will go here.
  2528. filename:        ds.b   15     ; File name for redirected output.    
  2529. program_name:    ds.l    1     ; Pointer to name in basepage command line.
  2530.                  ds.l   96     ; Program stack.
  2531. stack:           ds.l    0     ; Address of program stack.
  2532. program_end:     ds.l    0
  2533.  end
  2534.  
  2535.  
  2536. Execution results for PRG_5AP.TOS as a spawned process.
  2537.  
  2538. PRG_5AP.TOS Execution Results
  2539.  
  2540.   When executed from the desktop, this program will print this string on the
  2541.   video screen and pause for a keypress.  But, when this program is spawned by
  2542.   SPEED_1, SPEED_2, SPEED_3 or SPEEDTST, the string will be stored in a file 
  2543.   named PRG_5AP.DAT and the program will not pause for a  keypress.
  2544.    
  2545.   
  2546. Conclusion
  2547.   
  2548.      Performance testing and utilities with which such 
  2549. testing may be accomplished has been the subject of this 
  2550. chapter.  But the material in this chapter represents only a 
  2551. beginning.  Software testing as a subject is complicated 
  2552. enough, but implementing such testing is a horrendous task.
  2553.      At this point, I have provided you with a few simple 
  2554. tools and a tutorial which should assist you in calculating 
  2555. instruction execution times.  I have said that single-
  2556. stepping through a program with AssemPro's debugger is one 
  2557. method I use to verify a program's performance.  The 
  2558. debugger permits one to view registers and memory locations 
  2559. while tracing through a program in this manner.
  2560.      For short, uncomplicated programs, if you are able to 
  2561. keep your wits sharp while doing so, this is a viable method 
  2562. of verification.  But many programs cannot be tested within 
  2563. the debugger.  Furthermore, it is virtually impossible to 
  2564. keep track of register and memory activity for larger 
  2565. programs.  Therefore, programs which do this automatically 
  2566. will be introduced in a later chapter.
  2567.      For now, it is time to take advantage of the two 
  2568. utilities introduced here to investigate the questions 
  2569. raised by material in earlier chapters.  I do this in 
  2570. chapter 6.  There I will compare programs assembled in each 
  2571. of three assembly modes, and I will compare the performance 
  2572. of certain instructions, so that you can see early on why I 
  2573. choose to use them in future programs.
  2574.  
  2575.